diff --git a/.dockerignore b/.dockerignore index e391b818..f4d2b6d0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,11 +7,6 @@ _etc/ .gitignore .pre-commit-config.yaml .tool-versions -CODE_OF_CONDUCT.md -CONTRIBUTING.md Dockerfile LICENSE -pre-commit.ts README.md -SECURITY.md -version.txt diff --git a/.github/ARCHITECTURE.md b/.github/ARCHITECTURE.md new file mode 100644 index 00000000..bf255689 --- /dev/null +++ b/.github/ARCHITECTURE.md @@ -0,0 +1,5 @@ +# Architecture Guide + +This guide aims to explain how this codebase is organized. + +## File Structure diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..c3f98d63 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @eser diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9fae8aa8 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +# Code of Conduct + +See [@eser/directives](./pkg/directives/README.md) for our sets of rules and +recommendations we follow. diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 88% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index 84ba9dd3..fe383658 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -12,9 +12,9 @@ in a pull request. ### Code of Conduct This project and everyone participating in it is governed by the -[cool/directives](directives/README.md). By participating, you are expected to -uphold this code. Please report unacceptable behavior as in specified in the -link. +[@eser/directives](./pkg/directives/README.md). By participating, you are +expected to uphold this code. Please report unacceptable behavior as in +specified in the link. ### Technical Requirements @@ -53,7 +53,7 @@ from: ## Submitting a Pull Request -- Adhere to [cool/directives](directives/README.md) +- Adhere to [@eser/directives](./pkg/directives/README.md) - Fork the repo - Make your changes in a new branch - Provide tests for your changes, particularly for new features or fixes diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b4956819..686805d2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: Bug report -labels: bug +title: '' +labels: bug, needs triage assignees: '' - --- **Describe the bug**\ @@ -24,18 +23,11 @@ A clear and concise description of what you expected to happen. **Screenshots**\ If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - -- OS: [e.g. iOS] -- Browser [e.g. chrome, safari] -- Version [e.g. 22] - -**Smartphone (please complete the following information):** +**Environment (please complete the following information):** -- Device: [e.g. iPhone6] -- OS: [e.g. iOS8.1] -- Browser [e.g. stock browser, safari] -- Version [e.g. 22] +- OS: [e.g. Ubuntu 20.04, MacOS 11] +- Deno Version: +- Cool Version: **Additional context**\ Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 0a449ce7..5009c32d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea or voice a concern title: 'Feature request: ' labels: enhancement assignees: '' - --- **Is your feature request related to a problem? Please describe.**\ diff --git a/SECURITY.md b/.github/SECURITY.md similarity index 100% rename from SECURITY.md rename to .github/SECURITY.md diff --git a/typos.toml b/.github/typos.toml similarity index 100% rename from typos.toml rename to .github/typos.toml diff --git a/.gitignore b/.gitignore index 9347de2d..a0d61828 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,6 @@ Thumbs.db .env.local .env.*.local -# tool outputs -*.tsbuildinfo - # sensitive files *.key *.pem diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a32873cc..85ead5a7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files args: ["--maxkb=1024"] exclude: | (?x)^( - bundler/esbuild_v0.20.0.wasm + pkg/bundler/esbuild_v0.20.0.wasm )$ - id: check-case-conflict - id: check-executables-have-shebangs @@ -29,18 +29,19 @@ repos: args: ["--autofix", "--no-ensure-ascii", "--no-sort-keys"] - id: trailing-whitespace - repo: https://github.com/crate-ci/typos - rev: v1.16.23 + rev: v1.23.2 hooks: - id: typos verbose: true - args: [ - ] + args: + - "--config" + - ".github/typos.toml" exclude: | (?x)^( - docs/.* + docs/.*| )$ - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.0.0 + rev: v3.3.0 hooks: - id: conventional-pre-commit stages: [commit-msg] @@ -50,7 +51,7 @@ repos: - id: local-precommit name: local pre-commit tasks description: Runs local pre-commit tasks. - entry: bash -c 'deno run -A pre-commit.ts; git add -u' -- + entry: bash -c 'deno task script:validate-configs; git add -u' -- always_run: true pass_filenames: false language: system @@ -62,36 +63,29 @@ repos: exclude: | (?x)^( _etc/.*| - .github/FUNDING.yml| - .github/ISSUE_TEMPLATE/bug_report.md| - .github/ISSUE_TEMPLATE/feature_request.md| - .github/PULL_REQUEST_TEMPLATE.md| + .github/.*| .git/COMMIT_EDITMSG| - appserver/README.md| - bundler/README.md| - bundler/esbuild_v0.20.0.wasm| - collector/README.md| - di/README.md| - directives/README.md| docs/.*| - dotenv/README.md| - events/README.md| - file-loader/README.md| - fp/README.md| - functions/README.md| - jsx-runtime/README.md| - jsx-runtime.test/README.md| - lime/README.md| - lime.test/README.md| - parsing/README.md| - standards/README.md| - CODEOWNERS| - CODE_OF_CONDUCT.md| - CONTRIBUTING.md| + pkg/app-runtime/README.md| + pkg/bundler/README.md| + pkg/bundler/esbuild_v0.20.0.wasm| + pkg/collector/README.md| + pkg/config/README.md| + pkg/di/README.md| + pkg/directives/README.md| + pkg/dotenv/README.md| + pkg/events/README.md| + pkg/fp/README.md| + pkg/functions/README.md| + pkg/jsx-runtime/README.md| + pkg/jsx-runtime.test/README.md| + pkg/lime/README.md| + pkg/lime.test/README.md| + pkg/parsing/README.md| + pkg/standards/README.md| Dockerfile| LICENSE| - README.md| - SECURITY.md + README.md )$ - id: check-integrity name: check integrity diff --git a/.tool-versions b/.tool-versions index 265c7b2c..9193924e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -deno 1.41.3 -pre-commit 3.6.2 +deno 1.45.1 +pre-commit 3.7.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index cc790eeb..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,4 +0,0 @@ -# Code of Conduct - -See [cool/directives](directives/README.md) for our sets of rules and -recommendations we follow. diff --git a/Dockerfile b/Dockerfile index 65992b4e..8701e046 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ COPY deps.ts . RUN deno cache deps.ts # These steps will be re-run upon each file change in your working directory: -COPY ./ ./ +COPY ./pkg/ ./ # Compile the main app so that it doesn't need to be compiled each startup/entry. RUN deno cache mod.ts diff --git a/README.md b/README.md index ab511fb3..1199ec02 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,14 @@ strives to offer you an intuitive and delightful development experience. ### Component Set -| Component | Area | Description | -| --------------------------------- | ----------------- | --------------------------------------------------- | -| 📓 [cool/directives](directives/) | Rules | The ground rules adhered to by the entire ecosystem | -| 📑 [cool/standards](standards/) | Abstraction | Provides common abstraction layers for DI | -| ⚙️ [cool/di](di/) | Manager | Dependency injection system | -| 🧱 [cool/fp](fp/) | Functions Library | Tools for functional programming | -| 🔐 [cool/dotenv](dotenv/) | Manager | Load configurations from environment | -| 〰️ [cool/parsing](parsing/) | Manager | Parsing tools for various strings and streams | +| Component | Area | Description | +| -------------------------------------- | ----------------- | --------------------------------------------------- | +| 📓 [@eser/directives](pkg/directives/) | Rules | The ground rules adhered to by the entire ecosystem | +| 📑 [@eser/standards](pkg/standards/) | Abstraction | Provides common abstraction layers for DI | +| ⚙️ [@eser/di](pkg/di/) | Manager | Dependency injection system | +| 🧱 [@eser/fp](pkg/fp/) | Functions Library | Tools for functional programming | +| 🔐 [@eser/dotenv](pkg/dotenv/) | Manager | Load configurations from environment | +| 〰️ [@eser/parsing](pkg/parsing/) | Manager | Parsing tools for various strings and streams | Visit the respective component page for detailed usage instructions. @@ -78,7 +78,7 @@ We strive to run the following code seamlessly across [all platforms we support](#platform-support): ```js -import { Runtime, Context } from "$cool/runtime/mod.ts"; +import { Runtime, Context } from "@eser/runtime"; const home = (ctx: Context) => { return ctx.results.jsx(

Hello there!

); @@ -159,7 +159,7 @@ done. If you're going to report a bug or request a new feature, please ensure first that you comply with the conditions found under -[cool/directives](https://github.com/eser/cool/blob/dev/directives/README.md). +[@eser/directives](https://github.com/eser/cool/blob/dev/pkg/directives/README.md). After that, you can report an issue or request using [GitHub Issues](https://github.com/eser/cool/issues). Thanks in advance. diff --git a/_etc/tasks/check-license.ts b/_etc/tasks/check-license.ts deleted file mode 100644 index a74fa5b0..00000000 --- a/_etc/tasks/check-license.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -// Copied from $std/_tools/check_license.ts - -import * as runtime from "../../standards/runtime.ts"; -import { walk } from "./deps.ts"; - -const EXTENSIONS = ["*.js", ".ts", "*.jsx", ".tsx"]; - -const ROOT = new URL("../../", import.meta.url); -const CHECK = runtime.current.getArgs().includes("--check"); -const BASE_YEAR = "2023"; -// const CURRENT_YEAR = new Date().getFullYear(); -const RX_COPYRIGHT = new RegExp( - `// Copyright ([0-9]{4})-present Eser Ozvataf and other contributors\\. All rights reserved\\. ([0-9A-Za-z-.]+) license\\.\n`, -); -const COPYRIGHT = - `// Copyright ${BASE_YEAR}-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license.`; - -let failed = false; - -for await ( - const entry of walk.walk(ROOT, { - exts: EXTENSIONS, - skip: [ - /_etc\/coverage\/*$/, - /_etc\/temp\/*$/, - /_etc\/templates\/*$/, - /manifest\.gen\.ts$/, - ], - includeDirs: false, - }) -) { - const content = await runtime.current.readTextFile(entry.path); - const match = content.match(RX_COPYRIGHT); - - if (!match) { - if (CHECK) { - console.error(`Missing copyright header: ${entry.path}`); - failed = true; - } else { - const contentWithCopyright = COPYRIGHT + "\n" + content; - await runtime.current.writeTextFile(entry.path, contentWithCopyright); - console.log("Copyright header automatically added to " + entry.path); - } - } else if (match[1] !== BASE_YEAR) { - if (CHECK) { - console.error(`Incorrect copyright year: ${entry.path}`); - failed = true; - } else { - const index = match.index ?? 0; - const contentWithoutCopyright = content.replace(match[0], ""); - const contentWithCopyright = contentWithoutCopyright.substring(0, index) + - COPYRIGHT + "\n" + contentWithoutCopyright.substring(index); - await runtime.current.writeTextFile(entry.path, contentWithCopyright); - console.log("Copyright header automatically updated in " + entry.path); - } - } -} - -if (failed) { - console.info(`Copyright header should be "${COPYRIGHT}"`); - runtime.current.exit(1); -} diff --git a/_etc/tasks/deps.ts b/_etc/tasks/deps.ts deleted file mode 100644 index 03922a43..00000000 --- a/_etc/tasks/deps.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as walk from "jsr:@std/fs@0.220/walk"; diff --git a/appserver/appserver.ts b/appserver/appserver.ts deleted file mode 100644 index 42e51796..00000000 --- a/appserver/appserver.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import * as runModes from "../standards/run-modes.ts"; -import { events } from "../events/events.ts"; -import { di } from "../di/services.ts"; - -import { type Channel } from "./channel.ts"; -import { type Module } from "./module.ts"; - -export class AppServer { - static default = Symbol("default"); - - runMode: runModes.RunMode; - events: typeof events; - di: typeof di; - channels: Map; - modules: Map; - // deno-lint-ignore no-explicit-any - awaits: Array> = []; - - constructor() { - this.runMode = runModes.RunModes.NotSet; - this.events = events; - this.di = di; - this.channels = new Map(); - this.modules = new Map(); - } - - addModule(module: Module) { - const name = module.name ?? module.constructor.name; - - this.modules.set(name, module); - } - - addChannel(channel: Channel) { - const name = channel.name ?? channel.constructor.name; - - this.channels.set(name, channel); - } - - setAsDefaultAppServer() { - this.di.set(AppServer.default, this); - } - - async awaitAll() { - await Promise.all(this.awaits); - this.awaits.splice(0); - } - - // execute(_options: unknown) { - // } -} diff --git a/bundler/aot-snapshot.ts b/bundler/aot-snapshot.ts deleted file mode 100644 index 32e024f8..00000000 --- a/bundler/aot-snapshot.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import * as runtime from "../standards/runtime.ts"; -import { colors, path } from "./deps.ts"; -import { type BuildSnapshot, type BuildSnapshotJson } from "./mod.ts"; -import { setBuildId } from "./build-id.ts"; - -export class AotSnapshot implements BuildSnapshot { - #files: Map; - #dependencies: Map>; - - constructor( - files: Map, - dependencies: Map>, - ) { - this.#files = files; - this.#dependencies = dependencies; - } - - get paths(): Array { - return Array.from(this.#files.keys()); - } - - async read(pathStr: string): Promise | null> { - const filePath = this.#files.get(pathStr); - if (filePath !== undefined) { - try { - const file = await runtime.current.open(filePath, { read: true }); - return file.readable; - } catch (_err) { - return null; - } - } - - // Handler will turn this into a 404 - return null; - } - - dependencies(pathStr: string): Array { - return this.#dependencies.get(pathStr) ?? []; - } -} - -export async function loadAotSnapshot( - snapshotDirPath: string, -): Promise { - try { - if ((await runtime.current.stat(snapshotDirPath)).isDirectory) { - console.log( - `Using snapshot found at ${colors.cyan(snapshotDirPath)}`, - ); - - const snapshotPath = path.join(snapshotDirPath, "snapshot.json"); - const json = JSON.parse( - await runtime.current.readTextFile(snapshotPath), - ) as BuildSnapshotJson; - setBuildId(json.build_id); - - const dependencies = new Map>( - Object.entries(json.files), - ); - - const files = new Map(); - Object.keys(json.files).forEach((name) => { - const filePath = path.join(snapshotDirPath, name); - files.set(name, filePath); - }); - - return new AotSnapshot(files, dependencies); - } - return null; - } catch (err) { - if (!(err instanceof runtime.current.errors.NotFound)) { - throw err; - } - - return null; - } -} diff --git a/bundler/deps.ts b/bundler/deps.ts deleted file mode 100644 index 21a64e6f..00000000 --- a/bundler/deps.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as colors from "jsr:@std/fmt@0.220/colors"; -export * as hex from "jsr:@std/encoding@0.220/hex"; -export * as path from "jsr:@std/path@0.220"; -export * as regexpEscape from "jsr:@std/regexp@0.220/escape"; - -export * as esbuild from "npm:esbuild@0.20"; -// Import the WASM build on platforms where running subprocesses is not -// permitted, such as Deno Deploy, or when running without `--allow-run`. -// export * as esbuild from "npm:esbuild-wasm@0.20"; - -export { denoPlugins as esbuildDenoPlugins } from "jsr:@luca/esbuild-deno-loader@0.10"; diff --git a/collector/deps-dev.ts b/collector/deps-dev.ts deleted file mode 100644 index 1e79ac74..00000000 --- a/collector/deps-dev.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; diff --git a/collector/deps.ts b/collector/deps.ts deleted file mode 100644 index 61203922..00000000 --- a/collector/deps.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as path from "jsr:@std/path@0.220"; -export * as posix from "jsr:@std/path@0.220/posix"; -export * as walk from "jsr:@std/fs@0.220/walk"; diff --git a/collector/manifest.test.ts b/collector/manifest.test.ts deleted file mode 100644 index a3e84dbd..00000000 --- a/collector/manifest.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -// This file contains code from deno fresh (https://github.com/denoland/fresh), -// which is a web framework, licensed under the MIT license. - -// Copyright (c) 2023 Eser Ozvataf and other contributors -// Copyright (c) 2021-2023 Luca Casonato - -import { assert, bdd } from "./deps-dev.ts"; -import * as manifest from "./manifest.ts"; - -bdd.describe("cool/collector/manifest", () => { - bdd.it("specifierToIdentifier", () => { - const used = new Set(); - - assert.assertEquals( - manifest.specifierToIdentifier("foo/bar.ts", used), - "foo_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/bar.json.ts", used), - "foo_bar_json", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/[id]/bar", used), - "foo_id_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/[...all]/bar", used), - "foo_all_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/[[optional]]/bar", used), - "foo_optional_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/as-df/bar", used), - "foo_as_df_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/as@df", used), - "foo_as_df", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/foo.bar.baz.tsx", used), - "foo_foo_bar_baz", - ); - assert.assertEquals( - manifest.specifierToIdentifier("404", used), - "_404", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/_middleware", used), - "foo_middleware", - ); - }); - - bdd.it("specifierToIdentifier deals with duplicates", () => { - const used = new Set(); - - assert.assertEquals( - manifest.specifierToIdentifier("foo/bar", used), - "foo_bar", - ); - assert.assertEquals( - manifest.specifierToIdentifier("foo/bar", used), - "foo_bar_1", - ); - }); -}); diff --git a/deno.jsonc b/deno.jsonc index 187fdf2e..f6f4b9b1 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,7 +1,5 @@ { - "name": "@cool/cool", - "version": "0.7.17", - "exports": "./mod.ts", + "version": "0.7.20", "lock": false, "nodeModulesDir": false, "unstable": [ @@ -10,29 +8,29 @@ "temporal" ], "tasks": { - "cleanup": "rm -rf ./_etc/coverage/ ./_etc/coverage.lcov ./deno.lock && deno cache --reload ./mod.ts", - "check:license": "deno run --allow-read --allow-write ./_etc/tasks/check-license.ts", - "check:mod": "deno run --check --reload ./mod.ts", - "doc:lint": "deno doc --lint ./mod.ts", - "doc:generate": "rm -rf ./docs/ && deno doc --name='cool' --output=./docs/ --html ./mod.ts", + "cleanup": "rm -rf ./_etc/coverage/ ./_etc/coverage.lcov ./deno.lock && deno task check:mod", + "script:validate-licenses": "deno run --allow-read --allow-write ./scripts/validate-licenses.ts", + "script:validate-configs": "deno run --allow-net --allow-env --allow-read --allow-write ./scripts/validate-configs.ts", + "check:mod": "deno run --check --reload --allow-env --allow-read ./pkg/mod.ts", + "doc:lint": "deno doc --lint ./pkg/mod.ts", + "doc:generate": "rm -rf ./docs/ && deno doc --name='cool' --output=./docs/ --html ./pkg/mod.ts", "test": "DENO_KV_PATH=:memory: deno test --allow-sys --allow-net --allow-env --allow-read --allow-write --allow-run --parallel --watch", - "test:run": "DENO_KV_PATH=:memory: deno test --allow-sys --allow-net --allow-env --allow-read --allow-write --allow-run --parallel --trace-ops --coverage=./_etc/coverage/", + "test:run": "DENO_KV_PATH=:memory: deno test --allow-sys --allow-net --allow-env --allow-read --allow-write --allow-run --parallel --trace-leaks --coverage=./_etc/coverage/", "test:coverage": "deno coverage ./_etc/coverage/ --exclude='\\.(j|t)sx$'", "test:coverage-gen": "deno coverage ./_etc/coverage --exclude='\\.(j|t)sx$' --lcov --output=./_etc/coverage.lcov", - "ok": "deno fmt --check && deno lint && deno task check:license --check && deno task check:mod && deno task test:run", + "ok": "deno fmt --check && deno lint && deno task script:validate-licenses --check && deno task check:mod && deno task test:run", "repl": "deno repl --unstable-cron --unstable-kv --unstable-temporal --allow-all --eval-file=./repl-init.ts", "container:build": "docker build -t cool .", "container:run": "docker run --interactive --tty --rm cool" }, "compilerOptions": { "jsx": "precompile", - "jsxImportSource": "$cool", - + "jsxImportSource": "@eser", "strict": true, "noPropertyAccessFromIndexSignature": true, "noImplicitOverride": true, "noImplicitReturns": true, - + "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": false }, "lint": { @@ -40,14 +38,49 @@ "tags": [ "recommended" ] - } + }, + "include": [ + "ban-untagged-todo", + "camelcase", + "default-param-last", + "eqeqeq", + "no-await-in-loop", + "no-console", + "no-const-assign", + "no-eval", + "no-external-import", + "no-non-null-asserted-optional-chain", + "no-self-compare", + "no-sparse-arrays", + "no-sync-fn-in-async-fn", + "no-throw-literal", + "no-top-level-await", + "no-undef", + "prefer-ascii", + "single-var-declarator", + "verbatim-module-syntax" + ] }, "exclude": [ + ".git", "_etc/temp/", "docs/" ], - "imports": { - "$cool/jsx-runtime": "./jsx-runtime/mod.ts", - "$cool/": "./" - } + "workspace": [ + "./scripts", + "./pkg/app-runtime", + "./pkg/bundler", + "./pkg/collector", + "./pkg/config", + "./pkg/di", + "./pkg/events", + "./pkg/fp", + "./pkg/functions", + "./pkg/jsx-runtime", + "./pkg/jsx-runtime.test", + "./pkg/lime", + "./pkg/logging", + "./pkg/parsing", + "./pkg/standards" + ] } diff --git a/deps.ts b/deps.ts deleted file mode 100644 index 171d5271..00000000 --- a/deps.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as posix from "jsr:@std/path@0.220/posix"; -export * as jsonc from "jsr:@std/jsonc@0.220"; diff --git a/di/container.test.ts b/di/container.test.ts deleted file mode 100644 index 21dc908b..00000000 --- a/di/container.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd, mock } from "./deps-dev.ts"; -import { Registry } from "./container.ts"; - -bdd.describe("cool/di/container", () => { - bdd.it("non-existent key", () => { - const registry = new Registry(); - - const container = registry.build(); - - assert.assertStrictEquals(container.get("_"), undefined); - }); - - bdd.it("symbol key", () => { - const registry = new Registry(); - const a = Symbol("a"); - - registry.set(a, 6); - - const container = registry.build(); - - assert.assertStrictEquals(container.get(a), 6); - }); - - bdd.it("mixed keys", () => { - const registry = new Registry(); - - registry.set(Object.keys, "Object.keys method"); - registry.set(Object.values, "Object.values method"); - registry.set(Object.entries, "Object.entries method"); - - const container = registry.build(); - - assert.assertStrictEquals(container.get(Object.keys), "Object.keys method"); - assert.assertStrictEquals( - container.get(Object.values), - "Object.values method", - ); - assert.assertStrictEquals( - container.get(Object.entries), - "Object.entries method", - ); - }); - - bdd.it("singleton value", () => { - const registry = new Registry(); - - registry.set("b", 5); - - const container = registry.build(); - - assert.assertStrictEquals(container.get("b"), 5); - }); - - bdd.it("singleton nullables", () => { - const registry = new Registry(); - - registry.set("c", null); - registry.set("d", undefined); - - const container = registry.build(); - - assert.assertStrictEquals(container.get("c"), null); - assert.assertStrictEquals(container.get("d"), undefined); - assert.assertStrictEquals(container.get("e", "non-exists"), "non-exists"); - assert.assertStrictEquals(container.get("f"), undefined); - }); - - bdd.it("singleton functions", () => { - const registry = new Registry(); - - registry.set("g", (x: number) => x + 3); - - const container = registry.build(); - - const result = container.get("g"); - - assert.assertStrictEquals(result.constructor, Function); - assert.assertStrictEquals(result(5), 8); - }); - - bdd.it("lazy value", () => { - const registry = new Registry(); - - const spyFn = mock.spy(); - - let number = 1; - registry.setLazy("h", () => { - spyFn(); - return ++number; - }); - - const container = registry.build(); - - assert.assertStrictEquals(number, 1); - assert.assertStrictEquals(container.get("h"), 2); - assert.assertStrictEquals(number, 2); - - mock.assertSpyCalls(spyFn, 1); - }); - - bdd.it("lazy nullable", () => { - const registry = new Registry(); - - const iSpyFn = mock.spy(); - const jSpyFn = mock.spy(); - - registry.setLazy("i", () => { - iSpyFn(); - return null; - }); - - registry.setLazy("j", () => { - jSpyFn(); - return undefined; - }); - - const container = registry.build(); - - assert.assertStrictEquals(container.get("i"), null); - assert.assertStrictEquals(container.get("j"), undefined); - - mock.assertSpyCalls(iSpyFn, 1); - mock.assertSpyCalls(jSpyFn, 1); - }); - - bdd.it("scoped value", () => { - const registry = new Registry(); - - const kSpyFn = mock.spy(); - const lSpyFn = mock.spy(); - - let number = 1; - registry.setLazy("k", () => { - kSpyFn(); - return ++number; - }); - registry.setScoped("l", () => { - lSpyFn(); - return ++number; - }); - - const container = registry.build(); - const subScope = container.createScope(); - - assert.assertStrictEquals(number, 1); - assert.assertStrictEquals(container.get("k"), 2); - assert.assertStrictEquals(container.get("k"), 2); - assert.assertStrictEquals(container.get("l"), 3); - assert.assertStrictEquals(container.get("l"), 3); - assert.assertStrictEquals(subScope.get("k"), 2); - assert.assertStrictEquals(subScope.get("k"), 2); - assert.assertStrictEquals(subScope.get("l"), 4); - assert.assertStrictEquals(subScope.get("l"), 4); - assert.assertStrictEquals(container.get("l"), 3); - assert.assertStrictEquals(number, 4); - - mock.assertSpyCalls(kSpyFn, 1); - mock.assertSpyCalls(lSpyFn, 2); - }); - - bdd.it("transient functions", () => { - const registry = new Registry(); - - let number = 1; - registry.setTransient("m", () => number++); - - const container = registry.build(); - - assert.assertStrictEquals(container.get("m"), 1); - assert.assertStrictEquals(container.get("m"), 2); - assert.assertStrictEquals(container.get("m"), 3); - }); - - bdd.it("transient promise", async () => { - const registry = new Registry(); - - registry.setTransient("n", () => Promise.resolve("test")); - - const container = registry.build(); - - assert.assertStrictEquals(await container.get("n"), "test"); - }); - - bdd.it("getMany", () => { - const registry = new Registry(); - - const lazySpyFn = mock.spy(); - const transientSpyFn = mock.spy(); - - let number = 1; - registry.set("o", number); - registry.setLazy("p", () => { - lazySpyFn(); - return ++number; - }); - registry.setTransient("q", () => { - transientSpyFn(); - return ++number; - }); - - const container = registry.build(); - - assert.assertStrictEquals(number, 1); - - const [o, p, q, r, s] = container.getMany("o", "p", "q", "q", "s"); - - assert.assertStrictEquals(number, 4); - assert.assertStrictEquals(o, 1); - assert.assertStrictEquals(p, 2); - assert.assertStrictEquals(q, 3); - assert.assertStrictEquals(r, 4); - assert.assertStrictEquals(s, undefined); - - mock.assertSpyCalls(lazySpyFn, 1); - mock.assertSpyCalls(transientSpyFn, 2); - }); -}); diff --git a/di/deps-dev.ts b/di/deps-dev.ts deleted file mode 100644 index 350b70eb..00000000 --- a/di/deps-dev.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; -export * as mock from "jsr:@std/testing@0.220/mock"; diff --git a/di/fluent-api-factory.test.ts b/di/fluent-api-factory.test.ts deleted file mode 100644 index b124814e..00000000 --- a/di/fluent-api-factory.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd, mock } from "./deps-dev.ts"; -import { Registry } from "./container.ts"; -import { factory } from "./fluent-api-factory.ts"; - -const create = () => { - const registry = new Registry(); - - const services = registry.build(); - - const di = factory(services); - - return { di, registry, services }; -}; - -bdd.describe("cool/di/fluent-api-factory", () => { - bdd.it("di template strings: non-existent key", () => { - const { di } = create(); - - assert.assertStrictEquals(di`_`, undefined); - }); - - bdd.it("di template strings: singleton value", () => { - const { di } = create(); - - di.set("a", "value"); - - assert.assertStrictEquals(di`a`, "value"); - }); - - bdd.it("di template strings: literals", () => { - const { di } = create(); - - di.set("b", "c2"); - di.set("d", "e4"); - - assert.assertStrictEquals(di`${"b"} = ${"d"}.`, "c2 = e4."); - }); - - bdd.it("di.get(): non-string keys", () => { - const { di } = create(); - - const fnKey = () => null; - di.set(fnKey, "zz6"); - - const result = di.get(fnKey); - - assert.assertStrictEquals(result, "zz6"); - }); - - bdd.it("di.getMany()", () => { - const { di } = create(); - - di.set("f", "h3"); - di.set("g", "i5"); - - assert.assertEquals(di.getMany("f", "g"), ["h3", "i5"]); - }); - - bdd.it("di.invoke(): lambda", () => { - const { di } = create(); - - di.set("j", mock.spy()); - - // deno-fmt-ignore - const fn: (_: () => void) => void = j => j(); - - di.invoke(fn); - di.invoke(fn); - - mock.assertSpyCalls(di`j`, 2); - }); - - bdd.it("di.invoke(): lambda, multiple", () => { - const { di } = create(); - - di.set("k", mock.spy()); - di.set("l", mock.spy()); - di.set("m", mock.spy()); - - const fn = (k: () => void, l: () => void, m: () => void) => { - k(); - l(); - l(); - m(); - m(); - m(); - }; - - di.invoke(fn); - - mock.assertSpyCalls(di`k`, 1); - mock.assertSpyCalls(di`l`, 2); - mock.assertSpyCalls(di`m`, 3); - }); - - bdd.it("di.invoke(): anonymous function", () => { - const { di } = create(); - - di.set("n", mock.spy()); - - const fn = (n: () => void) => { - n(); - }; - - di.invoke(fn); - di.invoke(fn); - - mock.assertSpyCalls(di`n`, 2); - }); - - bdd.it("di.invoke(): anonymous function, multiple", () => { - const { di } = create(); - - di.set("o", mock.spy()); - di.set("p", mock.spy()); - di.set("q", mock.spy()); - - const fn = (o: () => void, p: () => void, q: () => void) => { - o(); - p(); - p(); - q(); - q(); - q(); - }; - - di.invoke(fn); - - mock.assertSpyCalls(di`o`, 1); - mock.assertSpyCalls(di`p`, 2); - mock.assertSpyCalls(di`q`, 3); - }); - - bdd.it("di.invoke(): named function", () => { - const { di } = create(); - - di.set("r", mock.spy()); - - const fn = (r: () => void) => { - r(); - }; - - di.invoke(fn); - di.invoke(fn); - - mock.assertSpyCalls(di`r`, 2); - }); - - bdd.it("di.invoke(): named function, multiple", () => { - const { di } = create(); - - di.set("s", mock.spy()); - di.set("t", mock.spy()); - di.set("u", mock.spy()); - - const fn = (s: () => void, t: () => void, u: () => void) => { - s(); - t(); - t(); - u(); - u(); - u(); - }; - - di.invoke(fn); - - mock.assertSpyCalls(di`s`, 1); - mock.assertSpyCalls(di`t`, 2); - mock.assertSpyCalls(di`u`, 3); - }); - - bdd.it("di.invoke(): generator function", () => { - const { di } = create(); - - di.set("v", mock.spy()); - - const fn = function* (v: () => void) { - yield v(); - }; - - for (const _ of di.invoke(fn)) { /* noop */ } - for (const _ of di.invoke(fn)) { /* noop */ } - - mock.assertSpyCalls(di`v`, 2); - }); - - bdd.it("di.invoke(): generator function, multiple", () => { - const { di } = create(); - - di.set("w", mock.spy()); - di.set("x", mock.spy()); - di.set("y", mock.spy()); - - const fn = function* (w: () => void, x: () => void, y: () => void) { - yield w(); - yield x(); - yield x(); - yield y(); - yield y(); - yield y(); - }; - - for (const _ of di.invoke(fn)) { /* noop */ } - - mock.assertSpyCalls(di`w`, 1); - mock.assertSpyCalls(di`x`, 2); - mock.assertSpyCalls(di`y`, 3); - }); - - bdd.it("di.invoke(): async function", async () => { - const { di } = create(); - - di.set("z", mock.spy()); - - const fn = async (z: () => void) => { - await Promise.resolve(z()); - }; - - await di.invoke(fn); - await di.invoke(fn); - - mock.assertSpyCalls(di`z`, 2); - }); - - bdd.it("di.invoke(): async function, multiple", async () => { - const { di } = create(); - - di.set("aa", mock.spy()); - di.set("ab", mock.spy()); - di.set("ac", mock.spy()); - - const fn = async ( - aa: () => void, - ab: () => void, - ac: () => void, - ) => { - await Promise.all([ - Promise.resolve(aa()), - Promise.resolve(ab()), - Promise.resolve(ab()), - Promise.resolve(ac()), - Promise.resolve(ac()), - Promise.resolve(ac()), - ]); - }; - - await di.invoke(fn); - - mock.assertSpyCalls(di`aa`, 1); - mock.assertSpyCalls(di`ab`, 2); - mock.assertSpyCalls(di`ac`, 3); - }); - - bdd.it("di.invoke(): async generator function", async () => { - const { di } = create(); - - di.set("ad", mock.spy()); - - const fn = async function* (ad: () => void) { - yield await Promise.resolve(ad()); - }; - - for await (const _ of di.invoke(fn)) { /* noop */ } - for await (const _ of di.invoke(fn)) { /* noop */ } - - mock.assertSpyCalls(di`ad`, 2); - }); - - bdd.it("di.invoke(): async generator function, multiple", async () => { - const { di } = create(); - - di.set("ae", mock.spy()); - di.set("af", mock.spy()); - di.set("ag", mock.spy()); - - const fn = async function* ( - ae: () => void, - af: () => void, - ag: () => void, - ) { - yield await Promise.resolve(ae()); - yield await Promise.resolve(af()); - yield await Promise.resolve(af()); - yield await Promise.resolve(ag()); - yield await Promise.resolve(ag()); - yield await Promise.resolve(ag()); - }; - - for await (const _ of di.invoke(fn)) { /* noop */ } - - mock.assertSpyCalls(di`ae`, 1); - mock.assertSpyCalls(di`af`, 2); - mock.assertSpyCalls(di`ag`, 3); - }); -}); diff --git a/di/invoker.test.ts b/di/invoker.test.ts deleted file mode 100644 index 9b3bad36..00000000 --- a/di/invoker.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { Registry } from "./container.ts"; -import { invoke } from "./invoker.ts"; - -const create = () => { - const registry = new Registry(); - - const services = registry.build(); - - return { registry, services }; -}; - -bdd.describe("cool/di/invoker", () => { - bdd.it("basic", () => { - const { registry, services } = create(); - - let count = 0; - - registry.set("singleton", "value1"); - registry.setLazy("lazy", () => "value2"); - registry.setTransient("transient", () => `value${++count + 2}`); - - const result = invoke(services, (singleton, lazy, transient) => { - return { singleton, lazy, transient }; - }); - - assert.assertEquals(result, { - singleton: "value1", - lazy: "value2", - transient: "value3", - }); - }); -}); diff --git a/di/services.ts b/di/services.ts deleted file mode 100644 index a7926809..00000000 --- a/di/services.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { type Factory, type ServiceScope } from "./primitives.ts"; -import { Registry } from "./container.ts"; -import { factory } from "./fluent-api-factory.ts"; - -export const registry: Registry = new Registry(); -export const services: ServiceScope = registry.build(); - -export const di: Factory = factory(services); - -export { di as default }; diff --git a/dotenv/deps.ts b/dotenv/deps.ts deleted file mode 100644 index 29baee53..00000000 --- a/dotenv/deps.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as dotenv from "jsr:@std/dotenv@0.220"; diff --git a/dotenv/mod.ts b/dotenv/mod.ts deleted file mode 100644 index 3484cb11..00000000 --- a/dotenv/mod.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * from "./base.ts"; -export * from "./loader.ts"; -export * from "./reader.ts"; -export * from "./configure.ts"; diff --git a/events/container.test.ts b/events/container.test.ts deleted file mode 100644 index be5797c2..00000000 --- a/events/container.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd, mock } from "./deps-dev.ts"; -import { Registry } from "./container.ts"; - -bdd.describe("cool/events/container", () => { - bdd.it("simple", () => { - const spyFn = mock.spy(); - - const registry = new Registry(); - registry.add("ev", () => spyFn()); - - const dispatcher = registry.build(); - dispatcher.dispatch("ev"); - - mock.assertSpyCalls(spyFn, 1); - }); - - bdd.it("multiple", () => { - const spyFn = mock.spy(); - - const registry = new Registry(); - registry.add("ev", () => spyFn()); - - const dispatcher = registry.build(); - dispatcher.dispatch("ev"); - dispatcher.dispatch("ev"); - - mock.assertSpyCalls(spyFn, 2); - }); - - bdd.it("once", () => { - const spyFn = mock.spy(); - - const registry = new Registry(); - registry.add("ev", () => spyFn(), { once: true }); - - const dispatcher = registry.build(); - dispatcher.dispatch("ev"); - dispatcher.dispatch("ev"); - - mock.assertSpyCalls(spyFn, 1); - }); - - bdd.it("parameters", () => { - const spyFn = mock.spy(); - - const registry = new Registry(); - registry.add("ev", (e) => spyFn(e)); - - const dispatcher = registry.build(); - dispatcher.dispatch("ev", { detail: "xx" }); - - mock.assertSpyCalls(spyFn, 1); - - const arg = spyFn.calls[0]?.args[0] as CustomEvent; - assert.assertEquals(arg.detail, "xx"); - }); - - bdd.it("no parameters", () => { - const spyFn = mock.spy(); - - const registry = new Registry(); - registry.add("ev", (e) => spyFn(e)); - - const dispatcher = registry.build(); - dispatcher.dispatch("ev"); - - mock.assertSpyCalls(spyFn, 1); - - const arg = spyFn.calls[0]?.args[0] as CustomEvent; - assert.assertStrictEquals(arg.detail, undefined); - }); -}); diff --git a/events/container.ts b/events/container.ts deleted file mode 100644 index 9b9d3a9c..00000000 --- a/events/container.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { type EventDispatcher, type EventRegistry } from "./primitives.ts"; - -export class Registry implements EventRegistry { - target: EventTarget = new EventTarget(); - - add( - type: string, - listener: EventListener, - options?: AddEventListenerOptions, - ): this { - this.target.addEventListener(type, listener, options); - - return this; - } - - remove( - type: string, - listener: EventListener, - options?: EventListenerOptions, - ): this { - this.target.removeEventListener(type, listener, options); - - return this; - } - - build(): EventDispatcher { - return new Dispatcher(this); - } -} - -export class Dispatcher implements EventDispatcher { - registry: EventRegistry; - - constructor(registry: EventRegistry) { - this.registry = registry; - } - - dispatch(type: string, eventInitDict?: CustomEventInit): boolean { - return this.registry.target.dispatchEvent( - new CustomEvent(type, eventInitDict), - ); - } -} diff --git a/events/deps-dev.ts b/events/deps-dev.ts deleted file mode 100644 index 350b70eb..00000000 --- a/events/deps-dev.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; -export * as mock from "jsr:@std/testing@0.220/mock"; diff --git a/file-loader/deps.ts b/file-loader/deps.ts deleted file mode 100644 index 297d91f3..00000000 --- a/file-loader/deps.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as jsonc from "jsr:@std/jsonc@0.220"; -export * as toml from "jsr:@std/toml@0.220"; -export * as yaml from "jsr:@std/yaml@0.220"; -export * as path from "jsr:@std/path@0.220"; -export * as fs from "jsr:@std/fs@0.220"; diff --git a/file-loader/loader.ts b/file-loader/loader.ts deleted file mode 100644 index 347ec85e..00000000 --- a/file-loader/loader.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import * as runtime from "../standards/runtime.ts"; -import { fs, jsonc, path, toml, yaml } from "./deps.ts"; - -// TODO(@eser) introduce strategy pattern for "search parents" and "recursive search" options - -export const locate = async ( - baseDir: string, - filenames: Array, - searchParents = false, -): Promise => { - let dir = baseDir; - - while (true) { - for (const name of filenames) { - const filepath = path.join(dir, name); - const isExists = await fs.exists(filepath, { isFile: true }); - - if (isExists) { - return filepath; - } - } - - if (!searchParents) { - break; - } - - const parent = path.dirname(dir); - if (parent === dir) { - break; - } - - dir = parent; - } - - return undefined; -}; - -export const parse = async ( - filepath: string, - extension?: string, -): Promise => { - const ext = extension ?? path.extname(filepath); - - const file = await runtime.current.readTextFile(filepath); - - if (ext === ".json") { - return JSON.parse(file) as T; - } - - if (ext === ".jsonc") { - return jsonc.parse(file) as T; - } - - if (ext === ".yaml" || ext === ".yml") { - return yaml.parse(file) as T; - } - - if (ext === ".toml") { - return toml.parse(file) as T; - } - - throw new Error(`Unsupported file extension: ${ext}`); -}; - -export type LoadResult = { - content: T | undefined; - path: string | undefined; -}; - -export const load = async ( - baseDir: string, - filenames: Array, - searchParents = false, -): Promise> => { - const filepath = await locate(baseDir, filenames, searchParents); - - if (filepath === undefined) { - return { - content: undefined, - path: undefined, - }; - } - - const result = await parse(filepath); - - return { - content: result, - path: filepath, - }; -}; diff --git a/fp/append-to-array.test.ts b/fp/append-to-array.test.ts deleted file mode 100644 index 4cd86de3..00000000 --- a/fp/append-to-array.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { appendToArray } from "./append-to-array.ts"; - -bdd.describe("cool/fp/append-to-array", () => { - bdd.it("basic", () => { - const arr1 = ["a", "b"]; - const str1 = "c"; - - const result = appendToArray(arr1, str1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, ["a", "b", "c"]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield "a"; - yield "b"; - }; - const str1 = "c"; - - const generated1 = gen1(); - const result = appendToArray(generated1, str1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, ["a", "b", "c"]); - }); -}); diff --git a/fp/append-to-object.test.ts b/fp/append-to-object.test.ts deleted file mode 100644 index 54e702e1..00000000 --- a/fp/append-to-object.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { appendToObject } from "./append-to-object.ts"; - -bdd.describe("cool/fp/append-to-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2 }; - const obj2 = { c: 3 }; - - const result = appendToObject(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertNotStrictEquals(result, obj2); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: 1, b: 2, c: 3 }); - }); -}); diff --git a/fp/associate-array.test.ts b/fp/associate-array.test.ts deleted file mode 100644 index bc5c09c6..00000000 --- a/fp/associate-array.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { associateArray } from "./associate-array.ts"; - -bdd.describe("cool/fp/associate-array", () => { - bdd.it("basic", () => { - const arr1 = [ - { id: 1, name: "foo" }, - { id: 2, name: "bar" }, - { id: 3, name: "baz" }, - ]; - const func1 = (value: { id: number }) => value.id; - - const result = associateArray(arr1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, arr1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { - 1: { id: 1, name: "foo" }, - 2: { id: 2, name: "bar" }, - 3: { id: 3, name: "baz" }, - }); - }); - - bdd.it("with-value-skipping", () => { - const arr1 = [ - { id: 1, name: "foo", skip: false }, - { id: 2, name: "bar", skip: false }, - { id: 3, name: "baz", skip: true }, - ]; - const func1 = (value: { id: number; skip: boolean }) => - value.skip ? undefined : value.id; - - const result = associateArray(arr1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, arr1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { - 1: { id: 1, name: "foo", skip: false }, - 2: { id: 2, name: "bar", skip: false }, - }); - }); -}); diff --git a/fp/associate-object.test.ts b/fp/associate-object.test.ts deleted file mode 100644 index f3c231a2..00000000 --- a/fp/associate-object.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { associateObject } from "./associate-object.ts"; - -bdd.describe("cool/fp/associate-object", () => { - bdd.it("basic", () => { - const obj1 = { - a: { id: 1, name: "foo" }, - b: { id: 2, name: "bar" }, - c: { id: 3, name: "baz" }, - }; - const func1 = (value: { id: number }) => value.id; - - const result = associateObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { - 1: { id: 1, name: "foo" }, - 2: { id: 2, name: "bar" }, - 3: { id: 3, name: "baz" }, - }); - }); - - bdd.it("with-value-skipping", () => { - const obj1 = { - a: { id: 1, name: "foo", skip: false }, - b: { id: 2, name: "bar", skip: false }, - c: { id: 3, name: "baz", skip: true }, - }; - const func1 = (value: { id: number; skip: boolean }) => - value.skip ? undefined : value.id; - - const result = associateObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { - 1: { id: 1, name: "foo", skip: false }, - 2: { id: 2, name: "bar", skip: false }, - }); - }); -}); diff --git a/fp/compose.test.ts b/fp/compose.test.ts deleted file mode 100644 index 4d2cc7ee..00000000 --- a/fp/compose.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { compose } from "./compose.ts"; - -bdd.describe("cool/fp/compose", () => { - bdd.it("basic", () => { - const lower = (x: string) => x.toLowerCase(); - const chars = (x: string) => x.replace(/[^\w \\-]+/g, ""); - const spaces = (x: string) => x.split(" "); - const dashes = (x: Array) => x.join("-"); - - const slug = compose(dashes, spaces, chars, lower); - - const result = slug("Hello World!"); - - assert.assertEquals(result, "hello-world"); - }); -}); diff --git a/fp/compose.ts b/fp/compose.ts deleted file mode 100644 index f9b1e9e6..00000000 --- a/fp/compose.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { type ArgList, type GenericFunction } from "../standards/functions.ts"; - -type ComposableFunction = GenericFunction; - -export const compose = ( - ...funcs: ReadonlyArray -): ComposableFunction => { - return funcs.reduce( - (previousFunction, currentFunction) => (...args: ArgList) => - previousFunction(currentFunction(...args)), - ); -}; - -export { compose as default }; diff --git a/fp/curry-right.test.ts b/fp/curry-right.test.ts deleted file mode 100644 index af56a032..00000000 --- a/fp/curry-right.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { curryRight } from "./curry-right.ts"; - -bdd.describe("cool/fp/curry-right", () => { - bdd.it("basic", () => { - const dec = (a: number, b: number) => a - b; - - const decWith5 = curryRight(dec, 5); - - const result = decWith5(3); - - assert.assertEquals(result, -2); - }); -}); diff --git a/fp/curry-right.ts b/fp/curry-right.ts deleted file mode 100644 index 9f50d73a..00000000 --- a/fp/curry-right.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export const curryRight = ( - func: (...args: readonly [...ReadonlyArray, ...ReadonlyArray]) => T3, - ...args: ReadonlyArray -): (...args: ReadonlyArray) => T3 => { - return (...args2: ReadonlyArray) => func(...args2, ...args); -}; - -export { curryRight as default }; diff --git a/fp/curry.test.ts b/fp/curry.test.ts deleted file mode 100644 index 62cabb67..00000000 --- a/fp/curry.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { curry } from "./curry.ts"; - -bdd.describe("cool/fp/curry", () => { - bdd.it("basic", () => { - const sum = (a: number, b: number) => a + b; - - const sumWith5 = curry(sum, 5); - - const result = sumWith5(3); - - assert.assertEquals(result, 8); - }); -}); diff --git a/fp/curry.ts b/fp/curry.ts deleted file mode 100644 index 1270464a..00000000 --- a/fp/curry.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export const curry = ( - func: (...args: readonly [...ReadonlyArray, ...ReadonlyArray]) => T3, - ...args: ReadonlyArray -): (...args: ReadonlyArray) => T3 => { - return (...args2: ReadonlyArray) => func(...args, ...args2); -}; - -export { curry as default }; diff --git a/fp/decorate.test.ts b/fp/decorate.test.ts deleted file mode 100644 index e1ba8001..00000000 --- a/fp/decorate.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { decorate } from "./decorate.ts"; - -bdd.describe("cool/fp/decorate", () => { - bdd.it("basic", () => { - let generator = () => 5; - - generator = decorate(generator, (x) => x() * 2); - generator = decorate(generator, (x) => x() + 1); - - const result = generator(); - - assert.assertEquals(result, 11); - }); - - bdd.it("parameters", () => { - let generator = (a: number) => a + 5; - - generator = decorate(generator, (x, a) => x(a) * 2); - generator = decorate(generator, (x, a) => x(a) + 1); - - const result = generator(3); - - assert.assertEquals(result, 17); - }); -}); diff --git a/fp/deep-copy.test.ts b/fp/deep-copy.test.ts deleted file mode 100644 index 1045b698..00000000 --- a/fp/deep-copy.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { deepCopy } from "./deep-copy.ts"; - -class Dummy { - prop: unknown; - - constructor(prop: unknown) { - this.prop = prop; - } -} - -bdd.describe("cool/fp/deep-copy", () => { - bdd.it("basic", () => { - const obj1 = { value: 5 }; - - const result = deepCopy(obj1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertStrictEquals(result.constructor, Object); - assert.assertEquals(result, obj1); - assert.assertEquals(result, { value: 5 }); - }); - - bdd.it("classes", () => { - const obj1 = new Dummy({ value: 5 }); - - const result = deepCopy(obj1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertStrictEquals(result.constructor, Dummy); - assert.assertEquals(result, obj1); - assert.assertEquals(result, new Dummy({ value: 5 })); - }); -}); diff --git a/fp/deep-merge.test.ts b/fp/deep-merge.test.ts deleted file mode 100644 index 258cf1fa..00000000 --- a/fp/deep-merge.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { deepMerge } from "./deep-merge.ts"; - -type Dummy1Prop = { - inners: { - inner1: number; - inner2: Array; - inner3?: number; - }; - outer?: string; -}; - -class Dummy1 { - prop: Dummy1Prop; - - constructor(prop: Dummy1Prop) { - this.prop = prop; - } -} - -type Dummy2Prop = { - inners: { - inner2: Array; - inner3: number; - }; - outer: string; -}; - -class Dummy2 { - prop: Dummy2Prop; - - constructor(prop: Dummy2Prop) { - this.prop = prop; - } -} - -bdd.describe("cool/fp/deep-merge", () => { - bdd.it("basic", () => { - const obj1 = { - a: { - b: [1, 2, 3], - c: { - d: 4, - }, - }, - }; - - const obj2 = { - a: { - b: [55], - }, - e: "hello", - }; - - const result = deepMerge(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertNotStrictEquals(result, obj2); - assert.assertStrictEquals(result.constructor, Object); - assert.assertEquals(result, { - a: { - b: [55], - c: { - d: 4, - }, - }, - e: "hello", - }); - }); - - bdd.it("classes", () => { - const obj1 = new Dummy1({ - inners: { - inner1: 1, - inner2: [2, 3], - }, - }); - - const obj2 = new Dummy2({ - inners: { - inner2: [4, 5], - inner3: 6, - }, - outer: "sub-mariner", - }); - - const result = deepMerge(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertNotStrictEquals(result, obj2); - assert.assertStrictEquals(result.constructor, Dummy1); - assert.assertEquals( - result, - new Dummy1({ - inners: { - inner1: 1, - inner2: [4, 5], - inner3: 6, - }, - outer: "sub-mariner", - }), - ); - }); -}); diff --git a/fp/deps-dev.ts b/fp/deps-dev.ts deleted file mode 100644 index 1e79ac74..00000000 --- a/fp/deps-dev.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; diff --git a/fp/dispatcher.test.ts b/fp/dispatcher.test.ts deleted file mode 100644 index abe81501..00000000 --- a/fp/dispatcher.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { dispatcher, type LogType, type NextType } from "./dispatcher.ts"; - -type StateType = Record; - -bdd.describe("cool/fp/dispatcher", () => { - bdd.it("basic", async () => { - const initialState = { quarter: 1, year: 2018, sum: 1 }; - - const actionAdd5 = (state: StateType, next: NextType) => - next({ ...state, sum: (state["sum"] ?? 0) + 5 }); - const actionDiv2 = (state: StateType, next: NextType) => - next({ ...state, sum: (state["sum"] ?? 0) / 2 }); - - const result = await dispatcher(initialState, [actionAdd5, actionDiv2]); - - assert.assertEquals(result, { quarter: 1, year: 2018, sum: 3 }); - }); - - bdd.it("logger", async () => { - const initialState = { quarter: 1, year: 2018, sum: 1 }; - - const actionAdd5 = (state: StateType, next: NextType) => - next({ ...state, sum: (state["sum"] ?? 0) + 5 }); - const actionDiv2 = (state: StateType, next: NextType) => - next({ ...state, sum: (state["sum"] ?? 0) / 2 }); - - const logs: Array> = []; - const logger = (entry: LogType) => logs.push(entry); - - const result = await dispatcher(initialState, [actionAdd5, actionDiv2], [ - logger, - ]); - - assert.assertEquals(result, { quarter: 1, year: 2018, sum: 3 }); - assert.assertEquals(logs, [ - { - action: "actionAdd5", - previousState: { quarter: 1, year: 2018, sum: 1 }, - newState: { quarter: 1, year: 2018, sum: 6 }, - }, - { - action: "actionDiv2", - previousState: { quarter: 1, year: 2018, sum: 6 }, - newState: { quarter: 1, year: 2018, sum: 3 }, - }, - ]); - }); - - bdd.it("promises", async () => { - const initialState = 0; - - const delay = (num: number) => - new Promise((resolve) => setTimeout(() => resolve(num), 200)); - const actionFirst = async (state: number, next: NextType) => - next(state + await delay(5)); - const actionSecond = async (state: number, next: NextType) => - next(state + await delay(3)); - - const result = await dispatcher(initialState, [actionFirst, actionSecond]); - - assert.assertEquals(result, 8); - }); -}); diff --git a/fp/distinct-array.test.ts b/fp/distinct-array.test.ts deleted file mode 100644 index 544f321c..00000000 --- a/fp/distinct-array.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { distinctArray } from "./distinct-array.ts"; - -bdd.describe("cool/fp/distinct-array", () => { - bdd.it("basic", () => { - const arr1 = [ - { id: 1, name: "foo", parent: 0 }, - { id: 2, name: "bar", parent: 1 }, - { id: 3, name: "baz", parent: 1 }, - ]; - const func1 = (item: { parent: number }) => item.parent; - - const result = distinctArray(arr1, func1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, [ - { id: 1, name: "foo", parent: 0 }, - { id: 2, name: "bar", parent: 1 }, - ]); - }); -}); diff --git a/fp/distinct-object.test.ts b/fp/distinct-object.test.ts deleted file mode 100644 index a22743b4..00000000 --- a/fp/distinct-object.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { distinctObject } from "./distinct-object.ts"; - -bdd.describe("cool/fp/distinct-object", () => { - bdd.it("basic", () => { - const obj1 = { - a: { id: 1, name: "foo", parent: 0 }, - b: { id: 2, name: "bar", parent: 1 }, - c: { id: 3, name: "baz", parent: 1 }, - }; - const func1 = (item: { parent: number }) => item.parent; - - const result = distinctObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { - a: { id: 1, name: "foo", parent: 0 }, - b: { id: 2, name: "bar", parent: 1 }, - }); - }); -}); diff --git a/fp/drop-from-array.test.ts b/fp/drop-from-array.test.ts deleted file mode 100644 index a00dd73a..00000000 --- a/fp/drop-from-array.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { dropFromArray } from "./drop-from-array.ts"; - -bdd.describe("cool/fp/drop-from-array", () => { - bdd.it("basic", () => { - const arr1 = ["a", "b", "c"]; - const int1 = 1; - - const result = dropFromArray(arr1, int1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 2); - assert.assertEquals(result, ["b", "c"]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield "a"; - yield "b"; - yield "c"; - }; - const int1 = 1; - - const generated1 = gen1(); - const result = dropFromArray(generated1, int1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 2); - assert.assertEquals(result, ["b", "c"]); - }); -}); diff --git a/fp/drop-from-object.test.ts b/fp/drop-from-object.test.ts deleted file mode 100644 index 076eba74..00000000 --- a/fp/drop-from-object.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { dropFromObject } from "./drop-from-object.ts"; - -bdd.describe("cool/fp/drop-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3 }; - const int1 = 1; - - const result = dropFromObject(obj1, int1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { b: 2, c: 3 }); - }); -}); diff --git a/fp/emitter.test.ts b/fp/emitter.test.ts deleted file mode 100644 index 0fc3c1ac..00000000 --- a/fp/emitter.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { emitter, type LogType } from "./emitter.ts"; - -bdd.describe("cool/fp/emitter", () => { - bdd.it("basic", async () => { - let sideEffectCounter = 0; - - const subscriberOne = () => { - sideEffectCounter += 1; - }; - const subscriberTwo = () => { - sideEffectCounter += 2; - }; - - const events = { - calculate: [subscriberOne, subscriberTwo], - }; - - await emitter(events, "calculate"); - - assert.assertEquals(sideEffectCounter, 3); - }); - - bdd.it("many events", async () => { - let sideEffectCounter = 0; - - const subscriberAddOne = () => { - sideEffectCounter += 1; - }; - const subscriberAddTwo = () => { - sideEffectCounter += 2; - }; - const subscriberSubOne = () => { - sideEffectCounter -= 1; - }; - - const events = { - add: [subscriberAddOne, subscriberAddTwo], - sub: [subscriberSubOne], - }; - - await emitter(events, "add"); - await emitter(events, "sub"); - - assert.assertEquals(sideEffectCounter, 2); - }); - - bdd.it("wildcard events", async () => { - let sideEffectCounter = 0; - - const subscriberOne = () => { - sideEffectCounter += 1; - }; - const subscriberTwo = () => { - sideEffectCounter -= 2; - }; - - const events = { - inc: [subscriberOne], - dec: [subscriberTwo], - }; - - await emitter(events, "*"); - - assert.assertEquals(sideEffectCounter, -1); - }); - - bdd.it("parameters", async () => { - let sideEffectCounter = 0; - - const subscriberOne = (value: number) => { - sideEffectCounter += value; - }; - const subscriberTwo = (value: number) => { - sideEffectCounter += value * 2; - }; - - const events = { - calculate: [subscriberOne, subscriberTwo], - }; - - await emitter(events, "calculate", [5]); - - assert.assertEquals(sideEffectCounter, 15); - }); - - bdd.it("subscribers", async () => { - let sideEffectCounter = 0; - - const subscriberOne = (value: number) => { - sideEffectCounter += value; - }; - const subscriberTwo = (value: number) => { - sideEffectCounter += value * 2; - }; - - const events = { - calculate: [subscriberOne, subscriberTwo], - }; - - const logs: Array = []; - const logger = (entry: LogType) => logs.push(entry); - - await emitter(events, "calculate", [5], [logger]); - - assert.assertEquals(sideEffectCounter, 15); - assert.assertEquals(logs, [ - { - event: "calculate", - subscriber: "subscriberOne", - args: [5], - }, - { - event: "calculate", - subscriber: "subscriberTwo", - args: [5], - }, - ]); - }); -}); diff --git a/fp/filter-array.test.ts b/fp/filter-array.test.ts deleted file mode 100644 index 498a10d7..00000000 --- a/fp/filter-array.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { filterArray } from "./filter-array.ts"; - -bdd.describe("cool/fp/filter-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const func1 = (x: number) => x <= 3; - - const result = filterArray(arr1, func1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 2, 3]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - const func1 = (x: number) => x <= 3; - - const generated1 = gen1(); - const result = filterArray(generated1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 2, 3]); - }); -}); diff --git a/fp/filter-object.test.ts b/fp/filter-object.test.ts deleted file mode 100644 index 4e8f5cf8..00000000 --- a/fp/filter-object.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { filterObject } from "./filter-object.ts"; - -bdd.describe("cool/fp/filter-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const func1 = (x: number) => x <= 3; - - const result = filterObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: 1, b: 2, c: 3 }); - }); -}); diff --git a/fp/iterate.test.ts b/fp/iterate.test.ts deleted file mode 100644 index c8efbd50..00000000 --- a/fp/iterate.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { pipe } from "./pipe.ts"; -import { iterate } from "./iterate.ts"; - -bdd.describe("cool/fp/iterate", () => { - bdd.it("basic", async () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - }; - - let total = 0; - - const func1 = (x: number) => { - total += x; - }; - - await iterate(gen1(), func1); - - assert.assertEquals(total, 6); - }); - - bdd.it("async", async () => { - const delay = (ms: number, value: number): Promise => - new Promise((resolve, _reject) => { - setTimeout( - () => resolve(value), - ms, - ); - }); - - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - }; - - let total = 0; - - const func1 = async (x: number) => { - total += await delay(10, x); - }; - - await iterate(gen1(), func1); - - assert.assertEquals(total, 6); - }); - - bdd.it("pipe", async () => { - const gen1 = function* () { - yield { value: 1 }; - yield { value: 2 }; - yield { value: 3 }; - }; - - let total = 0; - - const getValue = (x: { value: number }) => Promise.resolve(x.value); - const add5 = async (value: Promise) => await value + 5; - const sumWithTotal = async (value: Promise) => { - total += await value; - }; - - await iterate( - gen1(), - pipe( - getValue, - add5, - sumWithTotal, - ), - ); - - assert.assertEquals(total, 21); - }); -}); diff --git a/fp/map-array.test.ts b/fp/map-array.test.ts deleted file mode 100644 index 8c2e4483..00000000 --- a/fp/map-array.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { mapArray } from "./map-array.ts"; - -bdd.describe("cool/fp/map-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const func1 = (x: number) => x - 1; - - const result = mapArray(arr1, func1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [0, 1, 2, 3, 4]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - const func1 = (x: number) => x - 1; - - const generated1 = gen1(); - const result = mapArray(generated1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [0, 1, 2, 3, 4]); - }); -}); diff --git a/fp/map-object.test.ts b/fp/map-object.test.ts deleted file mode 100644 index 7f15bcc9..00000000 --- a/fp/map-object.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { mapObject } from "./map-object.ts"; - -bdd.describe("cool/fp/map-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const func1 = (value: number, key: string | number | symbol) => ({ - [key]: value - 1, - }); - - const result = mapObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 5); - assert.assertEquals(result, { a: 0, b: 1, c: 2, d: 3, e: 4 }); - }); - - bdd.it("with-value-skipping", () => { - const obj1 = { a: 1, b: 2, c: null }; - const func1 = ( - value: number | null, - key: string | number | symbol, - ) => { - if (value === null) { - return null; - } - - return { [key]: value - 1 }; - }; - - const result = mapObject(obj1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, obj1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { a: 0, b: 1 }); - }); -}); diff --git a/fp/match.test.ts b/fp/match.test.ts deleted file mode 100644 index 246e62cc..00000000 --- a/fp/match.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { match } from "./match.ts"; - -bdd.describe("cool/fp/match", () => { - bdd.it("basic", () => { - const str1 = "apple"; - - const result = match(str1, [ - ["apple", () => "Apple is selected."], - ["pear", () => "Pear is selected."], - ["banana", () => "Banana is selected."], - ]); - - assert.assertEquals( - result, - "Apple is selected.", - ); - }); - - bdd.it("conditions", () => { - const str1 = "appLe"; // intentionally mixed case - const str2 = "pear"; - const str3 = "banana"; - - const result = match(true, [ - // @ts-ignore - intentionally testing for falsey values - [str1 === "apple", () => "Apple is selected."], - [str2 === "pear", () => "Pear is selected."], - [str3 === "banana", () => "Banana is selected."], - ]); - - assert.assertEquals( - result, - "Pear is selected.", - ); - }); -}); diff --git a/fp/merge-arrays.test.ts b/fp/merge-arrays.test.ts deleted file mode 100644 index 0c3c049a..00000000 --- a/fp/merge-arrays.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { mergeArrays } from "./merge-arrays.ts"; - -bdd.describe("cool/fp/merge-arrays", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3]; - const arr2 = [4, 5]; - - const result = mergeArrays(arr1, arr2); - - assert.assertNotStrictEquals(result, arr1); - assert.assertNotStrictEquals(result, arr2); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [1, 2, 3, 4, 5]); - }); - - bdd.it("with-generator-1", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - }; - const arr1 = [4, 5]; - - const generated1 = gen1(); - const result = mergeArrays(generated1, arr1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [1, 2, 3, 4, 5]); - }); - - bdd.it("with-generator-2", () => { - const arr1 = [1, 2, 3]; - const gen1 = function* () { - yield 4; - yield 5; - }; - - const generated1 = gen1(); - const result = mergeArrays(arr1, generated1); - - assert.assertNotStrictEquals(result, arr1); - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [1, 2, 3, 4, 5]); - }); -}); diff --git a/fp/merge-objects.test.ts b/fp/merge-objects.test.ts deleted file mode 100644 index ca5a3711..00000000 --- a/fp/merge-objects.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { mergeObjects } from "./merge-objects.ts"; - -bdd.describe("cool/fp/merge-objects", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2 }; - const obj2 = { c: 3 }; - - const result = mergeObjects(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertNotStrictEquals(result, obj2); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: 1, b: 2, c: 3 }); - }); -}); diff --git a/fp/mutate.test.ts b/fp/mutate.test.ts deleted file mode 100644 index 1a279f51..00000000 --- a/fp/mutate.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { mutate } from "./mutate.ts"; - -bdd.describe("cool/fp/mutate", () => { - bdd.it("basic", () => { - const obj1 = { - firstName: "Eser", - lastName: "Ozvataf", - aliases: [], - }; - - const result = mutate(obj1, (x) => x.firstName = "Helo"); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals( - result, - { - firstName: "Helo", - lastName: "Ozvataf", - aliases: [], - }, - ); - }); - - bdd.it("array-push", () => { - const obj1 = { - firstName: "Eser", - lastName: "Ozvataf", - aliases: > [], - }; - - const result = mutate(obj1, (x) => { - x.firstName = "Helo"; - - x.aliases.push("laroux"); - }); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals( - result, - { - firstName: "Helo", - lastName: "Ozvataf", - aliases: [ - "laroux", - ], - }, - ); - }); - - bdd.it("with-class", () => { - class dummy { - items: Array; - - constructor() { - this.items = []; - } - } - - const obj1 = new dummy(); - - const result = mutate(obj1, (x) => { - x.items.push("laroux"); - }); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(result.constructor, dummy); - assert.assertEquals(Object.keys(result), ["items"]); - assert.assertEquals( - result.items, - [ - "laroux", - ], - ); - }); -}); diff --git a/fp/pick-from-array.test.ts b/fp/pick-from-array.test.ts deleted file mode 100644 index 5ba2da8a..00000000 --- a/fp/pick-from-array.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { pickFromArray } from "./pick-from-array.ts"; - -bdd.describe("cool/fp/pick-from-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const arr2 = [2, 3, 6]; - - const result = pickFromArray(arr1, arr2); - - assert.assertNotStrictEquals(result.items, arr1); - assert.assertNotStrictEquals(result.items, arr2); - assert.assertEquals(result.items.length, 2); - assert.assertEquals(result.items, [2, 3]); - - assert.assertNotStrictEquals(result.rest, arr1); - assert.assertNotStrictEquals(result.rest, arr2); - assert.assertEquals(result.rest.length, 3); - assert.assertEquals(result.rest, [1, 4, 5]); - }); - - bdd.it("with-generator-1", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - - const arr1 = [2, 3, 6]; - - const generated1 = gen1(); - const result = pickFromArray(generated1, arr1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.items, generated1); - assert.assertNotStrictEquals(result.items, arr1); - assert.assertEquals(result.items.length, 2); - assert.assertEquals(result.items, [2, 3]); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.rest, generated1); - assert.assertNotStrictEquals(result.rest, arr1); - assert.assertEquals(result.rest.length, 3); - assert.assertEquals(result.rest, [1, 4, 5]); - }); - - bdd.it("with-generator-2", () => { - const arr1 = [1, 2, 3, 4, 5]; - const gen1 = function* () { - yield 2; - yield 3; - yield 6; - }; - - const generated1 = gen1(); - const result = pickFromArray(arr1, generated1); - - assert.assertNotStrictEquals(result.items, arr1); - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.items, generated1); - assert.assertEquals(result.items.length, 2); - assert.assertEquals(result.items, [2, 3]); - - assert.assertNotStrictEquals(result.rest, arr1); - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.rest, generated1); - assert.assertEquals(result.rest.length, 3); - assert.assertEquals(result.rest, [1, 4, 5]); - }); -}); diff --git a/fp/pick-from-object.test.ts b/fp/pick-from-object.test.ts deleted file mode 100644 index 311165e0..00000000 --- a/fp/pick-from-object.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { pickFromObject } from "./pick-from-object.ts"; - -bdd.describe("cool/fp/pick-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const arr1 = ["b", "c", "f"]; - - const result = pickFromObject(obj1, arr1); - - assert.assertNotStrictEquals(result.items, obj1); - assert.assertEquals(Object.keys(result.items).length, 2); - assert.assertEquals(result.items, { b: 2, c: 3 }); - - assert.assertNotStrictEquals(result.rest, obj1); - assert.assertEquals(Object.keys(result.rest).length, 3); - assert.assertEquals(result.rest, { a: 1, d: 4, e: 5 }); - }); -}); diff --git a/fp/pipe.test.ts b/fp/pipe.test.ts deleted file mode 100644 index 2a36d90c..00000000 --- a/fp/pipe.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { pipe } from "./pipe.ts"; - -bdd.describe("cool/fp/pipe", () => { - bdd.it("basic", () => { - const lower = (x: string) => x.toLowerCase(); - const chars = (x: string) => x.replace(/[^\w \\-]+/g, ""); - const spaces = (x: string) => x.split(" "); - const dashes = (x: Array) => x.join("-"); - - const slug = pipe(lower, chars, spaces, dashes); - - const result = slug("Hello World!"); - - assert.assertEquals(result, "hello-world"); - }); -}); diff --git a/fp/pipe.ts b/fp/pipe.ts deleted file mode 100644 index 34f315cf..00000000 --- a/fp/pipe.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { type ArgList, type GenericFunction } from "../standards/functions.ts"; - -type ComposableFunction = GenericFunction; - -export const pipe = ( - ...funcs: ReadonlyArray -): ComposableFunction => { - return funcs.reduce( - (previousFunction, currentFunction) => (...args: ArgList) => - currentFunction(previousFunction(...args)), - ); -}; - -export { pipe as default }; diff --git a/fp/prepend-to-array.test.ts b/fp/prepend-to-array.test.ts deleted file mode 100644 index 84a57c31..00000000 --- a/fp/prepend-to-array.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { prependToArray } from "./prepend-to-array.ts"; - -bdd.describe("cool/fp/prepend-to-array", () => { - bdd.it("basic", () => { - const arr1 = ["b", "c"]; - const str1 = "a"; - - const result = prependToArray(arr1, str1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, ["a", "b", "c"]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield "b"; - yield "c"; - }; - const str1 = "a"; - - const generated1 = gen1(); - const result = prependToArray(generated1, str1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, ["a", "b", "c"]); - }); -}); diff --git a/fp/prepend-to-object.test.ts b/fp/prepend-to-object.test.ts deleted file mode 100644 index 580b625e..00000000 --- a/fp/prepend-to-object.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { prependToObject } from "./prepend-to-object.ts"; - -bdd.describe("cool/fp/prepend-to-object", () => { - bdd.it("basic", () => { - const obj1 = { b: 2, c: 3 }; - const obj2 = { a: 1 }; - - const result = prependToObject(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertNotStrictEquals(result, obj2); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: 1, b: 2, c: 3 }); - }); -}); diff --git a/fp/remove-first-match-from-array.test.ts b/fp/remove-first-match-from-array.test.ts deleted file mode 100644 index ab0f1e9d..00000000 --- a/fp/remove-first-match-from-array.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeFirstMatchFromArray } from "./remove-first-match-from-array.ts"; - -bdd.describe("cool/fp/remove-first-match-from-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 5, 2, 3, 4, 5]; - const func1 = (x: number) => x === 5; - - const result = removeFirstMatchFromArray(arr1, func1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [1, 2, 3, 4, 5]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 5; - yield 2; - yield 3; - yield 4; - yield 5; - }; - const func1 = (x: number) => x === 5; - - const generated1 = gen1(); - const result = removeFirstMatchFromArray(generated1, func1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [1, 2, 3, 4, 5]); - }); -}); diff --git a/fp/remove-first-match-from-object.test.ts b/fp/remove-first-match-from-object.test.ts deleted file mode 100644 index c49d7d89..00000000 --- a/fp/remove-first-match-from-object.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeFirstMatchFromObject } from "./remove-first-match-from-object.ts"; - -bdd.describe("cool/fp/remove-first-match-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, f: 5, b: 2, c: 3, d: 4, e: 5 }; - const func1 = (x: number) => x === 5; - - const result = removeFirstMatchFromObject(obj1, func1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 5); - assert.assertEquals(result, { a: 1, b: 2, c: 3, d: 4, e: 5 }); - }); -}); diff --git a/fp/remove-index-from-array.test.ts b/fp/remove-index-from-array.test.ts deleted file mode 100644 index e03f7bda..00000000 --- a/fp/remove-index-from-array.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeIndexFromArray } from "./remove-index-from-array.ts"; - -bdd.describe("cool/fp/remove-index-from-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const int1 = 2; - const int2 = 3; - - const result = removeIndexFromArray(arr1, int1, int2); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 2, 5]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - const int1 = 2; - const int2 = 3; - - const generated1 = gen1(); - const result = removeIndexFromArray(generated1, int1, int2); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 2, 5]); - }); -}); diff --git a/fp/remove-key-from-object.test.ts b/fp/remove-key-from-object.test.ts deleted file mode 100644 index 45548fa5..00000000 --- a/fp/remove-key-from-object.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeKeyFromObject } from "./remove-key-from-object.ts"; - -bdd.describe("cool/fp/remove-key-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const str1 = "b"; - const str2 = "c"; - - const result = removeKeyFromObject(obj1, str1, str2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: 1, d: 4, e: 5 }); - }); -}); diff --git a/fp/remove-value-from-array.test.ts b/fp/remove-value-from-array.test.ts deleted file mode 100644 index 86cd1064..00000000 --- a/fp/remove-value-from-array.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeValueFromArray } from "./remove-value-from-array.ts"; - -bdd.describe("cool/fp/remove-value-from-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const int1 = 2; - const int2 = 3; - - const result = removeValueFromArray(arr1, int1, int2); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 4, 5]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - const int1 = 2; - const int2 = 3; - - const generated1 = gen1(); - const result = removeValueFromArray(generated1, int1, int2); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 3); - assert.assertEquals(result, [1, 4, 5]); - }); -}); diff --git a/fp/remove-value-from-object.test.ts b/fp/remove-value-from-object.test.ts deleted file mode 100644 index 976163c7..00000000 --- a/fp/remove-value-from-object.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { removeValueFromObject } from "./remove-value-from-object.ts"; - -bdd.describe("cool/fp/remove-value-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: "Ia", b: "IIb", c: "IIIc", d: "IVd", e: "Ve" }; - const str1 = "IIb"; - const str2 = "IIIc"; - - const result = removeValueFromObject(obj1, str1, str2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { a: "Ia", d: "IVd", e: "Ve" }); - }); -}); diff --git a/fp/reverse-array.test.ts b/fp/reverse-array.test.ts deleted file mode 100644 index c48a4d59..00000000 --- a/fp/reverse-array.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { reverseArray } from "./reverse-array.ts"; - -bdd.describe("cool/fp/reverse-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - - const result = reverseArray(arr1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [5, 4, 3, 2, 1]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - - const generated1 = gen1(); - const result = reverseArray(generated1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 5); - assert.assertEquals(result, [5, 4, 3, 2, 1]); - }); -}); diff --git a/fp/reverse-object.test.ts b/fp/reverse-object.test.ts deleted file mode 100644 index 63d1747b..00000000 --- a/fp/reverse-object.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { reverseObject } from "./reverse-object.ts"; - -bdd.describe("cool/fp/reverse-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - - const result = reverseObject(obj1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 5); - assert.assertEquals(result, { e: 5, d: 4, c: 3, b: 2, a: 1 }); - }); -}); diff --git a/fp/split-array.test.ts b/fp/split-array.test.ts deleted file mode 100644 index 75148a9b..00000000 --- a/fp/split-array.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { splitArray } from "./split-array.ts"; - -bdd.describe("cool/fp/split-array", () => { - bdd.it("basic", () => { - const arr1 = [1, 2, 3, 4, 5]; - const int1 = 3; - - const result = splitArray(arr1, int1); - - assert.assertNotStrictEquals(result.items, arr1); - assert.assertEquals(result.items.length, 3); - assert.assertEquals(result.items, [1, 2, 3]); - - assert.assertNotStrictEquals(result.rest, arr1); - assert.assertEquals(result.rest.length, 2); - assert.assertEquals(result.rest, [4, 5]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield 1; - yield 2; - yield 3; - yield 4; - yield 5; - }; - - const int1 = 3; - - const generated1 = gen1(); - const result = splitArray(generated1, int1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.items, generated1); - assert.assertEquals(result.items.length, 3); - assert.assertEquals(result.items, [1, 2, 3]); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result.rest, generated1); - assert.assertEquals(result.rest.length, 2); - assert.assertEquals(result.rest, [4, 5]); - }); -}); diff --git a/fp/split-object.test.ts b/fp/split-object.test.ts deleted file mode 100644 index 7b709882..00000000 --- a/fp/split-object.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { splitObject } from "./split-object.ts"; - -bdd.describe("cool/fp/split-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const int1 = 3; - - const result = splitObject(obj1, int1); - - assert.assertNotStrictEquals(result.items, obj1); - assert.assertEquals(Object.keys(result.items).length, 3); - assert.assertEquals(result.items, { a: 1, b: 2, c: 3 }); - - assert.assertNotStrictEquals(result.rest, obj1); - assert.assertEquals(Object.keys(result.rest).length, 2); - assert.assertEquals(result.rest, { d: 4, e: 5 }); - }); -}); diff --git a/fp/take-from-array.test.ts b/fp/take-from-array.test.ts deleted file mode 100644 index b6256cc1..00000000 --- a/fp/take-from-array.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { takeFromArray } from "./take-from-array.ts"; - -bdd.describe("cool/fp/take-from-array", () => { - bdd.it("basic", () => { - const arr1 = ["a", "b", "c"]; - const int1 = 2; - - const result = takeFromArray(arr1, int1); - - assert.assertNotStrictEquals(result, arr1); - assert.assertEquals(result.length, 2); - assert.assertEquals(result, ["a", "b"]); - }); - - bdd.it("with-generator", () => { - const gen1 = function* () { - yield "a"; - yield "b"; - yield "c"; - }; - const int1 = 2; - - const generated1 = gen1(); - const result = takeFromArray(generated1, int1); - - // deno-lint-ignore no-explicit-any - assert.assertNotStrictEquals( result, generated1); - assert.assertEquals(result.length, 2); - assert.assertEquals(result, ["a", "b"]); - }); -}); diff --git a/fp/take-from-object.test.ts b/fp/take-from-object.test.ts deleted file mode 100644 index defbe804..00000000 --- a/fp/take-from-object.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { takeFromObject } from "./take-from-object.ts"; - -bdd.describe("cool/fp/take-from-object", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3 }; - const int1 = 2; - - const result = takeFromObject(obj1, int1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 2); - assert.assertEquals(result, { a: 1, b: 2 }); - }); -}); diff --git a/fp/wth.test.ts b/fp/wth.test.ts deleted file mode 100644 index 3095c869..00000000 --- a/fp/wth.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { wth } from "./wth.ts"; - -bdd.describe("cool/fp/wth", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const obj2 = { b: 6, f: 8 }; - - const result = wth(obj1, obj2); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 6); - assert.assertEquals(result, { a: 1, b: 6, c: 3, d: 4, e: 5, f: 8 }); - }); -}); diff --git a/fp/wthout.test.ts b/fp/wthout.test.ts deleted file mode 100644 index efdb3edb..00000000 --- a/fp/wthout.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { wthout } from "./wthout.ts"; - -bdd.describe("cool/fp/wthout", () => { - bdd.it("basic", () => { - const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; - const arr1 = ["a", "d"]; - - const result = wthout(obj1, ...arr1); - - assert.assertNotStrictEquals(result, obj1); - assert.assertEquals(Object.keys(result).length, 3); - assert.assertEquals(result, { b: 2, c: 3, e: 5 }); - }); -}); diff --git a/functions/deps-dev.ts b/functions/deps-dev.ts deleted file mode 100644 index 350b70eb..00000000 --- a/functions/deps-dev.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; -export * as mock from "jsr:@std/testing@0.220/mock"; diff --git a/functions/fn.test.ts b/functions/fn.test.ts deleted file mode 100644 index 550c8c1c..00000000 --- a/functions/fn.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd, mock } from "./deps-dev.ts"; -import { Ok, type Result } from "./results.ts"; -import { fn } from "./fn.ts"; - -bdd.describe("cool/functions/fn", () => { - bdd.it("simple fn().run()", async () => { - const spyFn = mock.spy(); - - const fns = fn>( - () => { - spyFn(); - - return Ok("Testing"); - }, - ); - - const result = await fns.run(); - - mock.assertSpyCalls(spyFn, 1); - assert.assertEquals(result[0]?.payload, "Testing"); - }); - - bdd.it("simple fn().iterate()", async () => { - const spyFn = mock.spy(); - - const fns = fn( - function* () { - spyFn(); - - yield Ok("hello"); - yield Ok("world"); - }, - ); - - const items = []; - for await (const item of fns.iterate()) { - items.push(item.payload); - } - - mock.assertSpyCalls(spyFn, 1); - assert.assertEquals(items, ["hello", "world"]); - }); - - bdd.it("multiple fn().iterate()", async () => { - const spyFn1 = mock.spy(); - const spyFn2 = mock.spy(); - - const fns = fn>( - async function* (c) { - spyFn1(); - - yield Ok("hello"); - yield* c.next(); - }, - async function* () { - spyFn2(); - - yield Ok("world"); - }, - ); - - const items = []; - for await (const item of fns.iterate()) { - items.push(item.payload); - } - - mock.assertSpyCalls(spyFn1, 1); - mock.assertSpyCalls(spyFn2, 1); - assert.assertEquals(items, ["hello", "world"]); - }); - - bdd.it("fn().use().run()", async () => { - const spyFn1 = mock.spy(); - const spyFn2 = mock.spy(); - - const fns = fn>() - .use( - async function* (c) { - spyFn1(); - - yield Ok("hello"); - yield* c.next(); - }, - ) - .use( - async function* () { - spyFn2(); - - yield Ok("world"); - }, - ); - - const items = await fns.run(); - - mock.assertSpyCalls(spyFn1, 1); - mock.assertSpyCalls(spyFn2, 1); - assert.assertEquals(items, [ - { payload: "hello" }, - { payload: "world" }, - ]); - }); - - bdd.it("alias fn()", async () => { - const spyFn = mock.spy(); - - const express = fn; - - const result = await express>() - .use( - async function* (c) { - spyFn(); - - yield Ok("Testing"); - yield* c.next(); - }, - ) - .run(); - - mock.assertSpyCalls(spyFn, 1); - assert.assertEquals(result[0]?.payload, "Testing"); - }); -}); diff --git a/jsx-runtime.test/deno.jsonc b/jsx-runtime.test/deno.jsonc deleted file mode 100644 index baf9c3f8..00000000 --- a/jsx-runtime.test/deno.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "jsx": "precompile", - "jsxImportSource": "$cool" - }, - "lock": false, - "imports": { - "$cool/jsx-runtime": "../jsx-runtime/mod.ts", - "$cool/": "../" - } -} diff --git a/jsx-runtime.test/deps-dev.ts b/jsx-runtime.test/deps-dev.ts deleted file mode 100644 index 350b70eb..00000000 --- a/jsx-runtime.test/deps-dev.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; -export * as mock from "jsr:@std/testing@0.220/mock"; diff --git a/jsx-runtime.test/root.test.tsx b/jsx-runtime.test/root.test.tsx deleted file mode 100644 index 596cf05a..00000000 --- a/jsx-runtime.test/root.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import * as jsxRuntimeInternal from "../jsx-runtime/index.js"; -import { assert, bdd, mock } from "./deps-dev.ts"; -import * as root from "./root.tsx"; - -bdd.describe("cool/jsx-runtime", () => { - bdd.it("should return the root component", () => { - const actual = root.Root(); - - assert.assertObjectMatch(actual, { - constructor: undefined, - type: root.Component, - props: { - "foo": "bar", - "lime-hack": true, - }, - key: undefined, - ref: undefined, - }); - }); - - bdd.it("should call the hook", () => { - const spyFn = mock.spy(); - - // deno-lint-ignore no-explicit-any - const hook = (vnode: any) => { - if (vnode.props["lime-hack"]) { - spyFn(); - } - }; - - // @ts-ignore typescript don't recognize this - jsxRuntimeInternal.options.tagHelperHook = hook; - - root.Root(); - - mock.assertSpyCalls(spyFn, 1); - - jsxRuntimeInternal.options.tagHelperHook = null; - }); -}); diff --git a/jsx-runtime/index.js b/jsx-runtime/index.js deleted file mode 100644 index 089a29f9..00000000 --- a/jsx-runtime/index.js +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -// This file contains code from preact (https://github.com/preactjs/preact), -// which is a React alternative, licensed under the MIT license. - -// Copyright (c) 2023 Eser Ozvataf and other contributors -// Copyright (c) 2015-present Jason Miller - -import { encodeEntities } from "./encoder.js"; - -export const elements = { - Fragment: null, -}; - -export const options = { - attr: null, - tagHelperHook: null, -}; - -export const IS_NON_DIMENSIONAL = - /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i; - -let vnodeId = 0; - -/** - * @fileoverview - * This file exports various methods that implement Babel's "automatic" JSX runtime API: - * - jsx(type, props, key) - * - jsxs(type, props, key) - * - jsxDEV(type, props, key, __source, __self) - * - * The implementation of createVNode here is optimized for performance. - * Benchmarks: https://esbench.com/bench/5f6b54a0b4632100a7dcd2b3 - */ - -/** - * JSX.Element factory used by Babel's {runtime:"automatic"} JSX transform - * @param {VNode['type']} type - * @param {VNode['props']} props - * @param {VNode['key']} [key] - * @param {unknown} [_isStaticChildren] - * @param {unknown} [__source] - * @param {unknown} [__self] - */ -export const jsx = (type, props, key, _isStaticChildren, __source, __self) => { - // We'll want to preserve `ref` in props to get rid of the need for - // forwardRef components in the future, but that should happen via - // a separate PR. - const normalizedProps = {}; - let ref, i; - - for (i in props) { - if (i == "ref") { - ref = props[i]; - continue; - } - - normalizedProps[i] = props[i]; - } - - /** @type {VNode & { __source: any; __self: any }} */ - const vnode = { - type, - props: normalizedProps, - key, - ref, - _children: null, - _parent: null, - _depth: 0, - _dom: null, - _nextDom: undefined, - _component: null, - constructor: undefined, - _original: --vnodeId, - _index: -1, - _flags: 0, - __source, - __self, - }; - - // If a Component VNode, check for and apply defaultProps. - // Note: `type` is often a String, and can be `undefined` in development. - if (typeof type === "function" && (ref = type.defaultProps)) { - for (i in ref) { - if (typeof normalizedProps[i] === "undefined") { - normalizedProps[i] = ref[i]; - } - } - } - - if (options.tagHelperHook) { - options.tagHelperHook(vnode); - } - - return vnode; -}; - -export const jsxs = jsx; -export const jsxDEV = jsx; - -/** - * Create a template vnode. This function is not expected to be - * used directly, but rather through a precompile JSX transform - * @param {string[]} templates - * @param {Array} exprs - * @returns {VNode} - */ -export const jsxTemplate = (templates, ...exprs) => { - const vnode = createVNode(elements.Fragment, { tpl: templates, exprs }); - // Bypass render to string top level Fragment optimization - vnode.key = vnode._vnode; - - return vnode; -}; - -const JS_TO_CSS = {}; -const CSS_REGEX = /[A-Z]/g; - -/** - * Serialize an HTML attribute to a string. This function is not - * expected to be used directly, but rather through a precompile - * JSX transform - * @param {string} name The attribute name - * @param {*} value The attribute value - * @returns {string} - */ -export const jsxAttr = (name, value) => { - if (options.attr) { - const result = options.attr(name, value); - - if (typeof result === "string") { - return result; - } - } - - if (name === "ref" || name === "key") { - return ""; - } - - if (name === "style" && typeof value === "object") { - let str = ""; - - for (const prop in value) { - const val = value[prop]; - - if (val != null && val !== "") { - const name = prop[0] == "-" ? prop : JS_TO_CSS[prop] || - (JS_TO_CSS[prop] = prop.replace(CSS_REGEX, "-$&").toLowerCase()); - - let suffix = ";"; - if ( - typeof val === "number" && - // Exclude custom-attributes - !name.startsWith("--") && - !IS_NON_DIMENSIONAL.test(name) - ) { - suffix = "px;"; - } - - str = `${str}${name}:${val}${suffix}`; - } - } - - return `${name}="${str}"`; - } - - if ( - value == null || - value === false || - typeof value === "function" || - typeof value === "object" - ) { - return ""; - } - - if (value === true) { - return name; - } - - return `${name}="${encodeEntities(value)}"`; -}; - -/** - * Escape a dynamic child passed to `jsxTemplate`. This function - * is not expected to be used directly, but rather through a - * precompile JSX transform - * @param {*} value - * @returns {string | null | VNode | Array} - */ -export const jsxEscape = (value) => { - if ( - value == null || - typeof value === "boolean" || - typeof value === "function" - ) { - return null; - } - - if (typeof value === "object") { - // Check for VNode - if (value.constructor === undefined) { - return value; - } - - if (Array.isArray(value)) { - for (let i = 0; i < value.length; i++) { - value[i] = jsxEscape(value[i]); - } - - return value; - } - } - - return encodeEntities("" + value); -}; diff --git a/lime/deps.ts b/lime/deps.ts deleted file mode 100644 index 65cc2bfe..00000000 --- a/lime/deps.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as posix from "jsr:@std/path@0.220/posix"; diff --git a/logging/logger.ts b/logging/logger.ts deleted file mode 100644 index 9aa34e1b..00000000 --- a/logging/logger.ts +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -// This file contains code from deno std lib (https://github.com/denoland/deno_std), -// which is a standard library, licensed under the MIT license. - -// Copyright (c) 2023-2024 Eser Ozvataf and other contributors -// Copyright (c) 2021-2023 the Deno authors - -import * as runtime from "../standards/runtime.ts"; -import * as logging from "../standards/logging.ts"; -import * as functions from "../standards/functions.ts"; -import * as formatters from "./formatters.ts"; - -export const DEFAULT_LEVEL = logging.Severities.Info; - -export type LogRecord = { - message: string; - args: functions.ArgList; - datetime: Date; - severity: logging.Severity; - loggerName: string; -}; - -export const Logger = class implements logging.Logger { - readonly loggerName: string; - readonly targetStream: WritableStream; - loglevel: logging.Severity; - readonly formatter: formatters.FormatterFn; - encoder: TextEncoder; - - constructor( - loggerName: string, - targetStream: WritableStream, - loglevel: logging.Severity = DEFAULT_LEVEL, - formatter: formatters.FormatterFn = formatters.jsonFormatter, - ) { - this.loggerName = loggerName; - this.targetStream = targetStream; - this.loglevel = loglevel; - this.formatter = formatter; - this.encoder = new TextEncoder(); - } - - /** - * If the level of the logger is greater than the level to log, then nothing - * is logged, otherwise a log record is passed to each log handler. `message` data - * passed in is returned. If a function is passed in, it is only evaluated - * if the message will be logged and the return value will be the result of the - * function, not the function itself, unless the function isn't called, in which - * case undefined is returned. All types are coerced to strings for logging. - */ - async log( - severity: logging.Severity, - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - if (this.loglevel > severity) { - return message instanceof Function ? undefined : message; - } - - let fnResult: T | undefined; - let logMessage: string; - if (message instanceof Function) { - fnResult = message(); - logMessage = this.asString(fnResult); - } else { - logMessage = this.asString(message); - } - const record: LogRecord = { - message: logMessage, - args: args, - datetime: new Date(), - severity: severity, - loggerName: this.loggerName, - }; - - const outputWriter = this.targetStream.getWriter(); - await outputWriter.ready; - - const formatted = this.formatter(record); - const encoded = this.encoder.encode(formatted); - await outputWriter.write(encoded); - - outputWriter.releaseLock(); - - return message instanceof Function ? fnResult : message; - } - - asString(data: unknown, isProperty = false): string { - if (typeof data === "string") { - if (isProperty) { - return `"${data}"`; - } - - return data; - } - - if ( - data === null || - typeof data === "number" || - typeof data === "bigint" || - typeof data === "boolean" || - typeof data === "undefined" || - typeof data === "symbol" - ) { - return String(data); - } - - if (data instanceof Error) { - return data.stack!; - } - - if (typeof data === "object") { - return `{${ - Object.entries(data) - .map(([k, v]) => `"${k}":${this.asString(v, true)}`) - .join(",") - }}`; - } - - return "undefined"; - } - - debug( - message: () => T, - ...args: functions.ArgList - ): Promise; - debug( - message: T extends functions.GenericFunction ? never : T, - ...args: functions.ArgList - ): Promise; - debug( - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - return this.log(logging.Severities.Debug, message, ...args); - } - - info(message: () => T, ...args: functions.ArgList): Promise; - info( - message: T extends functions.GenericFunction ? never : T, - ...args: functions.ArgList - ): Promise; - info( - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - return this.log(logging.Severities.Info, message, ...args); - } - - warn(message: () => T, ...args: functions.ArgList): Promise; - warn( - message: T extends functions.GenericFunction ? never : T, - ...args: functions.ArgList - ): Promise; - warn( - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - return this.log(logging.Severities.Warning, message, ...args); - } - - error( - message: () => T, - ...args: functions.ArgList - ): Promise; - error( - message: T extends functions.GenericFunction ? never : T, - ...args: functions.ArgList - ): Promise; - error( - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - return this.log(logging.Severities.Error, message, ...args); - } - - critical( - message: () => T, - ...args: functions.ArgList - ): Promise; - critical( - message: T extends functions.GenericFunction ? never : T, - ...args: functions.ArgList - ): Promise; - critical( - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList - ): Promise { - return this.log(logging.Severities.Critical, message, ...args); - } -}; - -export const current = new Logger( - "default", - runtime.current.getStdout(), - DEFAULT_LEVEL, -); diff --git a/parsing/deps-dev.ts b/parsing/deps-dev.ts deleted file mode 100644 index 1e79ac74..00000000 --- a/parsing/deps-dev.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export * as assert from "jsr:@std/assert@0.220"; -export * as bdd from "jsr:@std/testing@0.220/bdd"; diff --git a/parsing/lexer/lexer.test.ts b/parsing/lexer/lexer.test.ts deleted file mode 100644 index 19ba3476..00000000 --- a/parsing/lexer/lexer.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "../deps-dev.ts"; -import { simpleTokens } from "./tokens/simple.ts"; -import { Tokenizer } from "./lexer.ts"; - -bdd.describe("cool/parsing/lexer", () => { - bdd.it("operators and symbols", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = "=+-*_/%<>!&|?:;,.()[]{}^"; - - const result = Array.from(lexer.tokenizeFromString(input)); - - assert.assertEquals( - result, - [ - { kind: "T_EQUALS", value: "=" }, - { kind: "T_PLUS", value: "+" }, - { kind: "T_HYPHEN", value: "-" }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_UNDERSCORE", value: "_" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_PERCENT", value: "%" }, - { kind: "T_IS_SMALLER", value: "<" }, - { kind: "T_IS_GREATER", value: ">" }, - { kind: "T_EXCLAMATION_MARK", value: "!" }, - { kind: "T_AMPERSAND", value: "&" }, - { kind: "T_PIPE", value: "|" }, - { kind: "T_QUESTION_MARK", value: "?" }, - { kind: "T_COLON", value: ":" }, - { kind: "T_SEMICOLON", value: ";" }, - { kind: "T_COMMA", value: "," }, - { kind: "T_DOT", value: "." }, - { kind: "T_PARENTHESIS_OPEN", value: "(" }, - { kind: "T_PARENTHESIS_CLOSE", value: ")" }, - { kind: "T_SQUARE_BRACKET_OPEN", value: "[" }, - { kind: "T_SQUARE_BRACKET_CLOSE", value: "]" }, - { kind: "T_CURLY_BRACKET_OPEN", value: "{" }, - { kind: "T_CURLY_BRACKET_CLOSE", value: "}" }, - { kind: "T_CARET", value: "^" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("numbers", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = `1234 5678`; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_NUMERIC", value: "1234" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_NUMERIC", value: "5678" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("whitespace and comments", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = ` // This is a comment - /* This is a - multiline comment */ `; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "This" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "is" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "a" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "comment" }, - { kind: "T_NEWLINE", value: "\n" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "This" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "is" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "a" }, - { kind: "T_NEWLINE", value: "\n" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "multiline" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "comment" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("multiline comments", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = `/* test */`; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_SLASH", value: "/" }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "test" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("singleline comments", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = `// test`; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_SLASH", value: "/" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "test" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("mixed expression", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = `rocketLauncher++ /* Comment */ && test123`; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, - { kind: "T_PLUS", value: "+" }, - { kind: "T_PLUS", value: "+" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "Comment" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ASTERISK", value: "*" }, - { kind: "T_SLASH", value: "/" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_AMPERSAND", value: "&" }, - { kind: "T_AMPERSAND", value: "&" }, - { kind: "T_WHITESPACE", value: " " }, - { kind: "T_ALPHANUMERIC", value: "test123" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("alphanumeric from readable stream", async () => { - const lexer = new Tokenizer(simpleTokens); - - const readableStream = new ReadableStream({ - start(controller) { - controller.enqueue("rocket"); - controller.enqueue("Launcher"); - controller.close(); - }, - }); - - const result = []; - for await (const token of lexer.tokenize(readableStream)) { - result.push(token); - } - - assert.assertEquals( - result, - [ - { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("alphanumeric from string", () => { - const lexer = new Tokenizer(simpleTokens); - - const input = `rocketLauncher`; - - const result = Array.from(lexer.tokenizeFromString(input)); - assert.assertEquals( - result, - [ - { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, - { kind: "T_END", value: "" }, - ], - ); - }); - - bdd.it("alphanumeric from string, buffer boundary", () => { - const lexer = new Tokenizer(simpleTokens); - - lexer._reset(); - const result1 = Array.from(lexer._tokenizeChunk("rocket")); - - assert.assertEquals( - result1, - [], - ); - - const result2 = Array.from(lexer._tokenizeChunk("Launcher")); - assert.assertEquals( - result2, - [], - ); - - const result3 = Array.from(lexer._tokenizeChunk(null)); - assert.assertEquals( - result3, - [ - { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, - { kind: "T_END", value: "" }, - ], - ); - }); -}); diff --git a/parsing/parser.test.ts b/parsing/parser.test.ts deleted file mode 100644 index 257d0e01..00000000 --- a/parsing/parser.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { assert, bdd } from "./deps-dev.ts"; -import { simpleTokens } from "./lexer/tokens/simple.ts"; -import { Tokenizer } from "./lexer/lexer.ts"; -import { sequence, token } from "./parser.ts"; - -bdd.describe("cool/parsing/parser", () => { - bdd.it("tokens", () => { - const parser = token("T_NUMERIC"); - - const tokenizer = new Tokenizer(simpleTokens); - const tokenGenerator = tokenizer.tokenizeFromString("5"); - const result = parser(tokenGenerator); - - assert.assertEquals( - result, - [ - { kind: "token", token: { kind: "T_NUMERIC", value: "5" } }, - ], - ); - }); - - bdd.it("sequence", () => { - const parser = sequence( - token("T_NUMERIC"), - token("T_WHITESPACE"), - token("T_PLUS"), - token("T_WHITESPACE"), - token("T_NUMERIC"), - ); - - const tokenizer = new Tokenizer(simpleTokens); - const tokenGenerator = tokenizer.tokenizeFromString("1 + 2 * 3"); - const result = parser(tokenGenerator); - - assert.assertEquals( - result, - [ - { kind: "token", token: { kind: "T_NUMERIC", value: "1" } }, - { kind: "token", token: { kind: "T_WHITESPACE", value: " " } }, - { kind: "token", token: { kind: "T_PLUS", value: "+" } }, - { kind: "token", token: { kind: "T_WHITESPACE", value: " " } }, - { kind: "token", token: { kind: "T_NUMERIC", value: "2" } }, - ], - ); - }); -}); diff --git a/pkg/app-runtime/app-runtime.ts b/pkg/app-runtime/app-runtime.ts new file mode 100644 index 00000000..7c534ffb --- /dev/null +++ b/pkg/app-runtime/app-runtime.ts @@ -0,0 +1,62 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as runModes from "@eser/standards/run-modes"; +import * as events from "@eser/events"; +import * as services from "@eser/di/services"; + +import { type Channel } from "./channel.ts"; +import { type Module } from "./module.ts"; + +export type AppRuntimeState = { + runMode: runModes.RunMode; + events: events.Factory; + di: typeof services.di; + channels: Map; + modules: Map; + // deno-lint-ignore no-explicit-any + awaits: Array>; +}; + +export const createAppRuntimeState = (): AppRuntimeState => { + return { + runMode: runModes.RunModes.NotSet, + events: events.events, + di: services.di, + channels: new Map(), + modules: new Map(), + awaits: [], + }; +}; + +export class AppRuntime { + static default = Symbol("default"); + readonly state: S; + + constructor(state?: S) { + this.state = state ?? createAppRuntimeState() as S; + } + + addModule(module: Module) { + const name = module.name ?? module.constructor.name; + + this.state.modules.set(name, module); + } + + addChannel(channel: Channel) { + const name = channel.name ?? channel.constructor.name; + + this.state.channels.set(name, channel); + } + + setAsDefault() { + this.state.di.set(AppRuntime.default, this); + } + + async awaitAll() { + await Promise.all(this.state.awaits); + this.state.awaits.splice(0); + } + + // execute(_options: unknown) { + // } +} diff --git a/appserver/channel.ts b/pkg/app-runtime/channel.ts similarity index 100% rename from appserver/channel.ts rename to pkg/app-runtime/channel.ts diff --git a/pkg/app-runtime/deno.jsonc b/pkg/app-runtime/deno.jsonc new file mode 100644 index 00000000..84401a13 --- /dev/null +++ b/pkg/app-runtime/deno.jsonc @@ -0,0 +1,8 @@ +{ + "name": "@eser/app-runtime", + "version": "0.7.20", + "imports": {}, + "exports": { + ".": "./mod.ts" + } +} diff --git a/appserver/mod.ts b/pkg/app-runtime/mod.ts similarity index 79% rename from appserver/mod.ts rename to pkg/app-runtime/mod.ts index 2cfb68d1..368d4c51 100644 --- a/appserver/mod.ts +++ b/pkg/app-runtime/mod.ts @@ -1,4 +1,4 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. export * from "./module.ts"; -export * from "./appserver.ts"; +export * from "./app-runtime.ts"; diff --git a/appserver/module.ts b/pkg/app-runtime/module.ts similarity index 100% rename from appserver/module.ts rename to pkg/app-runtime/module.ts diff --git a/appserver/version-checker.ts b/pkg/app-runtime/version-checker.ts similarity index 66% rename from appserver/version-checker.ts rename to pkg/app-runtime/version-checker.ts index c21181ef..0577a66c 100644 --- a/appserver/version-checker.ts +++ b/pkg/app-runtime/version-checker.ts @@ -1,17 +1,17 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; -import { semver } from "./deps.ts"; +import * as semver from "@std/semver"; +import * as jsRuntime from "@eser/standards/js-runtime"; export const compareSemanticVersions = ( currentVersion: semver.SemVer, - targetVersion: semver.SemVerRange | semver.SemVer, + targetVersion: semver.Range | semver.SemVer, ) => { - if (semver.isSemVerRange(targetVersion)) { + if (semver.isRange(targetVersion)) { return semver.testRange(currentVersion, targetVersion); } - return !semver.gte(currentVersion, targetVersion); + return !semver.greaterOrEqual(currentVersion, targetVersion); }; export const compareTextVersions = ( @@ -25,5 +25,5 @@ export const compareTextVersions = ( }; export const checkMinDenoVersion = (minimumVersion: string) => { - return compareTextVersions(runtime.version.runtime, minimumVersion); + return compareTextVersions(jsRuntime.current.version, minimumVersion); }; diff --git a/pkg/bundler/aot-snapshot.ts b/pkg/bundler/aot-snapshot.ts new file mode 100644 index 00000000..221ebcfb --- /dev/null +++ b/pkg/bundler/aot-snapshot.ts @@ -0,0 +1,93 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as colors from "@std/fmt/colors"; +import * as posix from "@std/path/posix"; +import * as jsRuntime from "@eser/standards/js-runtime"; +import { type BuildSnapshot, type BuildSnapshotSerialized } from "./mod.ts"; +import { setBuildId } from "./build-id.ts"; + +export type AotSnapshotState = { + files: Map; + dependencyMapping: Map>; +}; + +export const createAotSnapshotState = ( + files: Map, + dependencyMapping: Map>, +): AotSnapshotState => { + return { + files: files, + dependencyMapping: dependencyMapping, + }; +}; + +export class AotSnapshot implements BuildSnapshot { + readonly state: AotSnapshotState; + + constructor(state: AotSnapshotState) { + this.state = state; + } + + get paths(): Array { + return Array.from(this.state.files.keys()); + } + + async read(pathStr: string): Promise | null> { + const filePath = this.state.files.get(pathStr); + + if (filePath !== undefined) { + try { + const file = await jsRuntime.current.open(filePath, { read: true }); + + return file.readable; + } catch (_err) { + return null; + } + } + + // Handler will turn this into a 404 + return null; + } + + dependencies(pathStr: string): Array { + return this.state.dependencyMapping.get(pathStr) ?? []; + } +} + +export const loadAotSnapshot = async ( + snapshotDirPath: string, +): Promise => { + try { + if (!(await jsRuntime.current.stat(snapshotDirPath)).isDirectory) { + return null; + } + + console.log( + `Using snapshot found at ${colors.cyan(snapshotDirPath)}`, + ); + + const snapshotPath = posix.join(snapshotDirPath, "snapshot.json"); + const json = JSON.parse( + await jsRuntime.current.readTextFile(snapshotPath), + ) as BuildSnapshotSerialized; + setBuildId(json.build_id); + + const dependencies = new Map>( + Object.entries(json.files), + ); + + const files = new Map(); + Object.keys(json.files).forEach((name) => { + const filePath = posix.join(snapshotDirPath, name); + files.set(name, filePath); + }); + + return new AotSnapshot(createAotSnapshotState(files, dependencies)); + } catch (err) { + if (!(err instanceof jsRuntime.current.errors.NotFound)) { + throw err; + } + + return null; + } +}; diff --git a/bundler/build-id.ts b/pkg/bundler/build-id.ts similarity index 67% rename from bundler/build-id.ts rename to pkg/bundler/build-id.ts index 41e5df85..fdcc30a2 100644 --- a/bundler/build-id.ts +++ b/pkg/bundler/build-id.ts @@ -1,9 +1,9 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; -import { hex } from "./deps.ts"; +import * as hex from "@std/encoding/hex"; +import * as jsRuntime from "@eser/standards/js-runtime"; -const env = runtime.current.getEnv(); +const env = jsRuntime.current.getEnv(); const deploymentId = env["DENO_DEPLOYMENT_ID"] || // For CI @@ -16,6 +16,6 @@ const buildIdHash = await crypto.subtle.digest( export let BUILD_ID = hex.encodeHex(buildIdHash); -export function setBuildId(buildId: string) { +export const setBuildId = (buildId: string) => { BUILD_ID = buildId; -} +}; diff --git a/pkg/bundler/deno.jsonc b/pkg/bundler/deno.jsonc new file mode 100644 index 00000000..f72594a4 --- /dev/null +++ b/pkg/bundler/deno.jsonc @@ -0,0 +1,15 @@ +{ + "name": "@eser/bundler", + "version": "0.7.20", + "imports": { + "@std/encoding": "jsr:@std/encoding@^1.0.1", + "@std/fmt": "jsr:@std/fmt@^0.225.6", + "@std/path": "jsr:@std/path@^1.0.0", + "@std/regexp": "jsr:@std/regexp@^1.0.0", + "@luca/esbuild-deno-loader": "jsr:@luca/esbuild-deno-loader@^0.10.3", + "esbuild": "npm:esbuild@^0.23.0" + }, + "exports": { + ".": "./mod.ts" + } +} diff --git a/bundler/esbuild.ts b/pkg/bundler/esbuild.ts similarity index 69% rename from bundler/esbuild.ts rename to pkg/bundler/esbuild.ts index 523880cc..22b0f89f 100644 --- a/bundler/esbuild.ts +++ b/pkg/bundler/esbuild.ts @@ -1,7 +1,14 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; -import { esbuild, esbuildDenoPlugins, path, regexpEscape } from "./deps.ts"; +import * as posix from "@std/path/posix"; +import * as regexpEscape from "@std/regexp/escape"; +import * as jsRuntime from "@eser/standards/js-runtime"; +import * as esbuild from "esbuild"; + +// Import the WASM build on platforms where running subprocesses is not +// permitted, such as Deno Deploy, or when running without `--allow-run`. +import { denoPlugins as esbuildDenoPlugins } from "@luca/esbuild-deno-loader"; + import { Builder, BuildSnapshot } from "./mod.ts"; // import { BUNDLE_PUBLIC_PATH } from "../server/constants.ts"; @@ -22,15 +29,27 @@ export type EsbuildBuilderOptions = { basePath?: string; }; +export type EsbuildBuilderState = { + options: EsbuildBuilderOptions; +}; + +export const createEsbuildBuilderState = ( + options: EsbuildBuilderOptions, +): EsbuildBuilderState => { + return { + options, + }; +}; + export class EsbuildBuilder implements Builder { - #options: EsbuildBuilderOptions; + readonly state: EsbuildBuilderState; - constructor(options: EsbuildBuilderOptions) { - this.#options = options; + constructor(state: EsbuildBuilderState) { + this.state = state; } - async build(): Promise { - const opts = this.#options; + async build(): Promise { + const opts = this.state.options; try { const absWorkingDir = opts.absoluteWorkingDir; @@ -49,7 +68,7 @@ export class EsbuildBuilder implements Builder { entryPoints: opts.entrypoints, platform: "browser", - target: this.#options.target, + target: opts.target, format: "esm", bundle: true, @@ -84,7 +103,7 @@ export class EsbuildBuilder implements Builder { const dependencies = new Map>(); for (const file of bundle.outputFiles) { - const relativePath = path.relative(absWorkingDir, file.path); + const relativePath = posix.relative(absWorkingDir, file.path); files.set(relativePath, file.contents); } @@ -102,14 +121,16 @@ export class EsbuildBuilder implements Builder { dependencies.set(pathStr, imports); } - return new EsbuildSnapshot(files, dependencies); + return new EsbuildSnapshot( + createEsbuildSnapshotState(files, dependencies), + ); } finally { esbuild.stop(); } } } -function devClientUrlPlugin(basePath?: string): esbuild.Plugin { +const devClientUrlPlugin = (basePath?: string): esbuild.Plugin => { return { name: "dev-client-url", setup(build) { @@ -117,7 +138,7 @@ function devClientUrlPlugin(basePath?: string): esbuild.Plugin { { filter: /client\.ts$/, namespace: "file" }, async (args) => { // Load the original script - const contents = await runtime.current.readTextFile(args.path); + const contents = await jsRuntime.current.readTextFile(args.path); // Replace the URL const modifiedContents = contents.replace( @@ -133,15 +154,15 @@ function devClientUrlPlugin(basePath?: string): esbuild.Plugin { ); }, }; -} +}; -function buildIdPlugin(buildId: string): esbuild.Plugin { +const buildIdPlugin = (buildId: string): esbuild.Plugin => { const file = import.meta.resolve("../runtime/build-id.ts"); const url = new URL(file); let options: esbuild.OnLoadOptions; if (url.protocol === "file:") { - const pathNormalized = path.fromFileUrl(url); + const pathNormalized = posix.fromFileUrl(url); const filter = new RegExp(`^${regexpEscape.escape(pathNormalized)}$`); options = { filter, namespace: "file" }; } else { @@ -160,29 +181,39 @@ function buildIdPlugin(buildId: string): esbuild.Plugin { ); }, }; -} +}; + +export type EsbuildSnapshotState = { + files: Map; + dependencyMapping: Map>; +}; + +export const createEsbuildSnapshotState = ( + files: Map, + dependencies: Map>, +): EsbuildSnapshotState => { + return { + files, + dependencyMapping: dependencies, + }; +}; export class EsbuildSnapshot implements BuildSnapshot { - #files: Map; - #dependencies: Map>; - - constructor( - files: Map, - dependencies: Map>, - ) { - this.#files = files; - this.#dependencies = dependencies; + readonly state: EsbuildSnapshotState; + + constructor(state: EsbuildSnapshotState) { + this.state = state; } get paths(): Array { - return Array.from(this.#files.keys()); + return Array.from(this.state.files.keys()); } read(pathStr: string): Uint8Array | null { - return this.#files.get(pathStr) ?? null; + return this.state.files.get(pathStr) ?? null; } dependencies(pathStr: string): Array { - return this.#dependencies.get(pathStr) ?? []; + return this.state.dependencyMapping.get(pathStr) ?? []; } } diff --git a/jsx-runtime/mod.ts b/pkg/bundler/mod.ts similarity index 51% rename from jsx-runtime/mod.ts rename to pkg/bundler/mod.ts index 3d4e880e..8c48b102 100644 --- a/jsx-runtime/mod.ts +++ b/pkg/bundler/mod.ts @@ -1,3 +1,5 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -export { jsx, jsxAttr, jsxEscape, jsxTemplate } from "./index.js"; +export * from "./primitives.ts"; +export * from "./esbuild.ts"; +export * from "./aot-snapshot.ts"; diff --git a/bundler/mod.ts b/pkg/bundler/primitives.ts similarity index 81% rename from bundler/mod.ts rename to pkg/bundler/primitives.ts index 4fb9c93b..8625c559 100644 --- a/bundler/mod.ts +++ b/pkg/bundler/primitives.ts @@ -1,12 +1,5 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -export { - EsbuildBuilder, - type EsbuildBuilderOptions, - EsbuildSnapshot, -} from "./esbuild.ts"; -export { AotSnapshot } from "./aot-snapshot.ts"; - export interface Builder { build(): Promise; } @@ -31,7 +24,7 @@ export type BuildSnapshot = { dependencies(pathStr: string): Array; }; -export type BuildSnapshotJson = { +export type BuildSnapshotSerialized = { build_id: string; files: Record>; }; diff --git a/collector/collector.ts b/pkg/collector/collector.ts similarity index 78% rename from collector/collector.ts rename to pkg/collector/collector.ts index be5d2c64..cdfb2acd 100644 --- a/collector/collector.ts +++ b/pkg/collector/collector.ts @@ -6,15 +6,15 @@ // Copyright (c) 2023 Eser Ozvataf and other contributors // Copyright (c) 2021-2023 Luca Casonato -import { path, walk } from "./deps.ts"; -import * as patterns from "../standards/patterns.ts"; -// import { runtime } from "$cool/standards/mod.ts"; +import * as posix from "@std/path/posix"; +import * as walk from "@std/fs/walk"; +import * as patterns from "@eser/standards/patterns"; export async function* walkFiles( baseDir: string, globFilter: string | undefined, ignoreFilePattern: RegExp, -) { +): AsyncGenerator { const routesFolder = walk.walk(baseDir, { includeDirs: false, includeFiles: true, @@ -23,9 +23,9 @@ export async function* walkFiles( }); for await (const entry of routesFolder) { - const rel = path.relative(baseDir, entry.path); + const rel = posix.relative(baseDir, entry.path); - if (globFilter !== undefined && !path.globToRegExp(globFilter).test(rel)) { + if (globFilter !== undefined && !posix.globToRegExp(globFilter).test(rel)) { continue; } @@ -40,12 +40,16 @@ export type CollectExportsOptions = { ignoreFilePattern?: RegExp; }; -export const collectExports = async (options: CollectExportsOptions) => { +export type ExportItem = [string, Array<[string, unknown]>]; + +export const collectExports = async ( + options: CollectExportsOptions, +): Promise> => { // const mainModule = runtime.current.getMainModule(); const ignoreFilePattern = options.ignoreFilePattern ?? patterns.JS_TEST_FILE_PATTERN; - const exports: Array<[string, Array<[string, unknown]>]> = []; + const exports: Array = []; for await ( const entry of walkFiles( diff --git a/pkg/collector/deno.jsonc b/pkg/collector/deno.jsonc new file mode 100644 index 00000000..54d59132 --- /dev/null +++ b/pkg/collector/deno.jsonc @@ -0,0 +1,12 @@ +{ + "name": "@eser/collector", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/path": "jsr:@std/path@^1.0.0", + "@std/fs": "jsr:@std/fs@^0.229.3" + }, + "exports": { + ".": "./mod.ts" + } +} diff --git a/collector/formatter.ts b/pkg/collector/formatter.ts similarity index 85% rename from collector/formatter.ts rename to pkg/collector/formatter.ts index 08a959ff..8015f192 100644 --- a/collector/formatter.ts +++ b/pkg/collector/formatter.ts @@ -6,10 +6,10 @@ // Copyright (c) 2023 Eser Ozvataf and other contributors // Copyright (c) 2021-2023 Luca Casonato -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; export const format = async (input: string) => { - const proc = new runtime.current.Command(runtime.current.execPath(), { + const proc = new jsRuntime.current.Command(jsRuntime.current.execPath(), { args: ["fmt", "-"], stdin: "piped", stdout: "piped", diff --git a/pkg/collector/manifest.test.ts b/pkg/collector/manifest.test.ts new file mode 100644 index 00000000..55725ca1 --- /dev/null +++ b/pkg/collector/manifest.test.ts @@ -0,0 +1,68 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +// This file contains code from deno fresh (https://github.com/denoland/fresh), +// which is a web framework, licensed under the MIT license. + +// Copyright (c) 2023 Eser Ozvataf and other contributors +// Copyright (c) 2021-2023 Luca Casonato + +import * as assert from "@std/assert"; +import * as manifest from "./manifest.ts"; + +Deno.test("specifierToIdentifier", () => { + const used = new Set(); + + assert.assertEquals( + manifest.specifierToIdentifier("foo/bar.ts", used), + "foo_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/bar.json.ts", used), + "foo_bar_json", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/[id]/bar", used), + "foo_id_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/[...all]/bar", used), + "foo_all_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/[[optional]]/bar", used), + "foo_optional_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/as-df/bar", used), + "foo_as_df_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/as@df", used), + "foo_as_df", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/foo.bar.baz.tsx", used), + "foo_foo_bar_baz", + ); + assert.assertEquals( + manifest.specifierToIdentifier("404", used), + "_404", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/_middleware", used), + "foo_middleware", + ); +}); + +Deno.test("specifierToIdentifier deals with duplicates", () => { + const used = new Set(); + + assert.assertEquals( + manifest.specifierToIdentifier("foo/bar", used), + "foo_bar", + ); + assert.assertEquals( + manifest.specifierToIdentifier("foo/bar", used), + "foo_bar_1", + ); +}); diff --git a/collector/manifest.ts b/pkg/collector/manifest.ts similarity index 91% rename from collector/manifest.ts rename to pkg/collector/manifest.ts index 3de76db4..07983bd6 100644 --- a/collector/manifest.ts +++ b/pkg/collector/manifest.ts @@ -6,9 +6,9 @@ // Copyright (c) 2023 Eser Ozvataf and other contributors // Copyright (c) 2021-2023 Luca Casonato -import { path, posix } from "./deps.ts"; -import * as runtime from "../standards/runtime.ts"; -import * as logger from "../logging/logger.ts"; +import * as posix from "@std/path/posix"; +import * as jsRuntime from "@eser/standards/js-runtime"; +import * as logger from "@eser/logging/logger"; import * as validatorIdentifier from "./validator-identifier/mod.ts"; import * as collector from "./collector.ts"; import * as formatter from "./formatter.ts"; @@ -35,9 +35,12 @@ const toImportSpecifier = (file: string) => { // valid file names in Windows, macOS and Linux and every identifier we // create here will be prefixed with at least one "$". This greatly // simplifies the invalid characters we have to account for. -export const specifierToIdentifier = (specifier: string, used: Set) => { +export const specifierToIdentifier = ( + specifier: string, + used: Set, +): string => { // specifier = specifier.replace(/^(?:\.\/pkg)\//, ""); - const ext = path.extname(specifier); + const ext = posix.extname(specifier); if (ext) { specifier = specifier.slice(0, -ext.length); } @@ -88,11 +91,11 @@ const placeholder = (text: string) => { export const writeManifestToString = async ( collection: Array<[string, Array<[string, unknown]>]>, -) => { +): Promise => { const sortFn = getSortFn(); const used = new Set(); - const imports = []; + const imports: Array = []; const manifest = { // baseUrl: placeholder("import.meta.url"), exports: [] as Array, @@ -141,7 +144,7 @@ export const manifest = ${manifestSerialized}; export const buildManifest = async ( target: WritableStream, options: collector.CollectExportsOptions, -) => { +): Promise => { const collection = await collector.collectExports(options); const manifestStr = await writeManifestToString(collection); @@ -168,8 +171,8 @@ export const buildManifest = async ( export const buildManifestFile = async ( filepath: string, options: collector.CollectExportsOptions, -) => { - const target = await runtime.current.open(filepath, { +): Promise => { + const target = await jsRuntime.current.open(filepath, { create: true, write: true, }); diff --git a/collector/mod.ts b/pkg/collector/mod.ts similarity index 100% rename from collector/mod.ts rename to pkg/collector/mod.ts diff --git a/collector/validator-identifier/char-codes.ts b/pkg/collector/validator-identifier/char-codes.ts similarity index 100% rename from collector/validator-identifier/char-codes.ts rename to pkg/collector/validator-identifier/char-codes.ts diff --git a/collector/validator-identifier/mod.ts b/pkg/collector/validator-identifier/mod.ts similarity index 99% rename from collector/validator-identifier/mod.ts rename to pkg/collector/validator-identifier/mod.ts index a7bdc482..7784471d 100644 --- a/collector/validator-identifier/mod.ts +++ b/pkg/collector/validator-identifier/mod.ts @@ -27,12 +27,12 @@ const isInAstralSet = (code: number, set: readonly number[]) => { let pos = 0x10000; for (let i = 0, length = set.length; i < length; i += 2) { - pos += set[i]; + pos += set[i]!; if (pos > code) { return false; } - pos += set[i + 1]; + pos += set[i + 1]!; if (pos >= code) { return true; } diff --git a/dotenv/README.md b/pkg/config/README.md similarity index 80% rename from dotenv/README.md rename to pkg/config/README.md index 98d828b8..d4203755 100644 --- a/dotenv/README.md +++ b/pkg/config/README.md @@ -1,6 +1,6 @@ -# 🔐 [cool/dotenv](./) +# 🔐 [@eser/config](./) -`cool/dotenv` helps you load configurations from `.env.*` files and environment +`@eser/config` helps you load configurations from `.env.*` files and environment variables, a practice based on **The 12-Factor App** methodology which recommends separating configuration from code. @@ -20,10 +20,10 @@ The 12-Factor App is a set of best practices designed to enable applications to be built with portability and resilience when deployed to the web. [Learn more about The 12-Factor App](https://12factor.net/). -## 🤔 What cool/dotenv offers? +## 🤔 What @eser/config offers? -`cool/dotenv` helps you handle these configurations properly. With using -`cool/dotenv`, the environment variables are loaded from the following sources: +`@eser/config` helps you handle these configurations properly. With using +`@eser/config`, the environment variables are loaded from the following sources: - Environment variables that passed to the Deno process. - Environment variables defined by the system's shell. @@ -40,7 +40,7 @@ from file or environment variable) takes precedence. That means you can use the ## 🛠 Usage and API Reference -Here you'll find a list of features provided by `cool/dotenv` along with brief +Here you'll find a list of features provided by `@eser/config` along with brief descriptions and usage examples. ### Loading environment variables @@ -48,7 +48,7 @@ descriptions and usage examples. **Basic usage:** ```js -import { load } from "$cool/dotenv/mod.ts"; +import { load } from "@eser/config"; const vars = await load(); console.log(vars); @@ -57,7 +57,7 @@ console.log(vars); **Load from different directory:** ```js -import { load } from "$cool/dotenv/mod.ts"; +import { load } from "@eser/config"; const vars = await load({ baseDir: "./config" }); console.log(vars); @@ -68,7 +68,7 @@ console.log(vars); **Basic usage:** ```js -import { configure, env } from "$cool/dotenv/mod.ts"; +import { configure, env } from "@eser/config"; const options = await configure( (reader, acc) => { @@ -84,7 +84,7 @@ const options = await configure( **With custom interfaces:** ```js -import { configure, env } from "$cool/dotenv/mod.ts"; +import { configure, env } from "@eser/config"; type Options = { env: string; diff --git a/pkg/config/config.test.ts b/pkg/config/config.test.ts new file mode 100644 index 00000000..b9e7f670 --- /dev/null +++ b/pkg/config/config.test.ts @@ -0,0 +1,32 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import * as config from "./mod.ts"; + +// TODO FileLoader: constructor'u transformer alacak, CONSTANTS veya AS_IS gibi. +// TODO FileLoader.File: config file'ları "json", "toml", "yaml" ve ".env" gibi farklı formatlarda olabilecekler. +// TODO FileLoader.FileStack: config file'lar bir load order ile environment'a göre farklı stackler oluşturabilecekler. +// TODO FileLoader.FileStack: olmayan dosyalar atlanabilecek. + +// TODO: Config Source FileLoader: FileLoader kullanan bir config source olacak. +// TODO: Config Source Environment: Environment variable'ları kullanan bir config source olacak. +// TODO: her config source'un kendini refresh ettiği bir TTL'si olacak (default: infinite0, bir süre geçtikten sonra veya invalidate() komutu ile bu resetlenebilecek. + +Deno.test("new Config", () => { + const result = new config.Config(); + + assert.assertInstanceOf(result, config.Config); +}); + +Deno.test("setting config meta", () => { + const result = new config.Config(); + + result.setKeyMeta("sampleKey", { + description: "sample description", + type: "string", + ttl: 1000, + disallowSource: ["env"], + }); + + assert.assertInstanceOf(result, config.Config); +}); diff --git a/pkg/config/config.ts b/pkg/config/config.ts new file mode 100644 index 00000000..f6e4f5aa --- /dev/null +++ b/pkg/config/config.ts @@ -0,0 +1,25 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as primitives from "./primitives.ts"; + +export type ConfigState = { + meta: Record; +}; + +export const createConfigState = (): ConfigState => { + return { + meta: {}, + }; +}; + +export class Config { + readonly state: ConfigState; + + constructor(state?: ConfigState) { + this.state = state ?? createConfigState(); + } + + setKeyMeta(key: string, meta: primitives.ConfigItemMeta) { + this.state.meta[key] = meta; + } +} diff --git a/pkg/config/deno.jsonc b/pkg/config/deno.jsonc new file mode 100644 index 00000000..aa6a0f13 --- /dev/null +++ b/pkg/config/deno.jsonc @@ -0,0 +1,17 @@ +{ + "name": "@eser/config", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/collections": "jsr:@std/collections@^1.0.4", + "@std/fs": "jsr:@std/fs@^0.229.3", + "@std/jsonc": "jsr:@std/jsonc@^0.224.3", + "@std/path": "jsr:@std/path@^1.0.0", + "@std/toml": "jsr:@std/toml@^1.0.0", + "@std/yaml": "jsr:@std/yaml@^0.224.3" + }, + "exports": { + ".": "./mod.ts", + "./file": "./file/mod.ts" + } +} diff --git a/pkg/config/dotenv-source.ts b/pkg/config/dotenv-source.ts new file mode 100644 index 00000000..befcbb87 --- /dev/null +++ b/pkg/config/dotenv-source.ts @@ -0,0 +1,51 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as primitives from "./primitives.ts"; + +export type DotenvSourceState = primitives.SourceState; + +export const createDotenvSourceState = (): DotenvSourceState => { + return { + id: "dotenv", + }; +}; + +export class DotenvSource implements primitives.Source { + readonly state: DotenvSourceState; + + constructor(state?: DotenvSourceState) { + this.state = state ?? createDotenvSourceState(); + } + + getBooleanValue( + _flagKey: string, + defaultValue: boolean, + _requestContext?: primitives.RequestContext, + ): Promise { + return Promise.resolve(defaultValue); + } + + getNumberValue( + _flagKey: string, + defaultValue: T, + _requestContext?: primitives.RequestContext, + ): Promise { + return Promise.resolve(defaultValue); + } + + getObjectValue( + _flagKey: string, + defaultValue: T, + _requestContext?: primitives.RequestContext, + ): Promise { + return Promise.resolve(defaultValue); + } + + getStringValue( + _flagKey: string, + defaultValue: T, + _requestContext?: primitives.RequestContext, + ): Promise { + return Promise.resolve(defaultValue); + } +} diff --git a/dotenv/base.ts b/pkg/config/dotenv/base.ts similarity index 100% rename from dotenv/base.ts rename to pkg/config/dotenv/base.ts diff --git a/dotenv/configure.ts b/pkg/config/dotenv/configure.ts similarity index 90% rename from dotenv/configure.ts rename to pkg/config/dotenv/configure.ts index 11991bfb..de3d84e4 100644 --- a/dotenv/configure.ts +++ b/pkg/config/dotenv/configure.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; +import * as promises from "@eser/standards/promises"; import { env } from "./base.ts"; import { load, type LoaderOptions } from "./loader.ts"; import { createEnvReader, type EnvReader } from "./reader.ts"; @@ -15,7 +15,7 @@ export type EnvVariables = BaseEnvVariables & Record; export type ConfigureFn = ( reader: EnvReader, target: T, -) => Promisable; +) => promises.Promisable; // public functions export const configure = async ( diff --git a/dotenv/loader.ts b/pkg/config/dotenv/loader.ts similarity index 87% rename from dotenv/loader.ts rename to pkg/config/dotenv/loader.ts index c5d598cd..b1ca3cea 100644 --- a/dotenv/loader.ts +++ b/pkg/config/dotenv/loader.ts @@ -1,7 +1,7 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; -import { dotenv } from "./deps.ts"; +import * as dotenv from "@std/dotenv"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { defaultEnvValue, defaultEnvVar, env, type EnvMap } from "./base.ts"; // type definitions @@ -22,7 +22,7 @@ export const parseEnvFromFile = async ( filepath: string, ): Promise> => { try { - const data = await runtime.current.readFile(filepath); + const data = await jsRuntime.current.readFile(filepath); const decoded = new TextDecoder("utf-8").decode(data); const escaped = decodeURIComponent(decoded); @@ -30,7 +30,7 @@ export const parseEnvFromFile = async ( return result; } catch (e) { - if (e instanceof runtime.current.errors.NotFound) { + if (e instanceof jsRuntime.current.errors.NotFound) { return {}; } @@ -50,7 +50,7 @@ export const load = async ( options, ); - const sysVars = runtime.current.getEnv(); + const sysVars = jsRuntime.current.getEnv(); const envName = sysVars[options_.defaultEnvVar] ?? options_.defaultEnvValue; const vars = new Map(); diff --git a/dotenv/reader.ts b/pkg/config/dotenv/reader.ts similarity index 100% rename from dotenv/reader.ts rename to pkg/config/dotenv/reader.ts diff --git a/pkg/config/file/loader.ts b/pkg/config/file/loader.ts new file mode 100644 index 00000000..2d102816 --- /dev/null +++ b/pkg/config/file/loader.ts @@ -0,0 +1,145 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as fs from "@std/fs"; +import * as jsonc from "@std/jsonc"; +import * as posix from "@std/path/posix"; +import * as toml from "@std/toml"; +import * as yaml from "@std/yaml"; +import * as jsRuntime from "@eser/standards/js-runtime"; +import * as primitives from "./primitives.ts"; + +// TODO(@eser) introduce strategy pattern for "search parents" and "recursive search" options + +export const locate = async ( + baseDir: string, + filenames: Array, + searchParents = false, +): Promise => { + let dir = baseDir; + + while (true) { + for (const name of filenames) { + const filepath = posix.join(dir, name); + const isExists = await fs.exists(filepath, { isFile: true }); + + if (isExists) { + return filepath; + } + } + + if (!searchParents) { + break; + } + + const parent = posix.dirname(dir); + if (parent === dir) { + break; + } + + dir = parent; + } + + return undefined; +}; + +export const getFileFormat = (filepath: string): primitives.FileFormat => { + const ext = posix.extname(filepath); + + if (ext === ".json") { + return primitives.FileFormats.Json; + } + + if (ext === ".jsonc") { + return primitives.FileFormats.JsonWithComments; + } + + if (ext === ".yaml" || ext === ".yml") { + return primitives.FileFormats.Yaml; + } + + if (ext === ".toml") { + return primitives.FileFormats.Toml; + } + + if (ext === ".env") { + return primitives.FileFormats.EnvironmentFile; + } + + return primitives.FileFormats.Unknown; +}; + +export type ParseResult = { + content: T | undefined; + filepath: string | undefined; + format: primitives.FileFormat; +}; + +export const parse = async ( + filepath: string, + format?: primitives.FileFormat, +): Promise> => { + const formatFinal = format ?? getFileFormat(filepath); + + const textContent = await jsRuntime.current.readTextFile(filepath); + + if (formatFinal === primitives.FileFormats.Json) { + return { + content: JSON.parse(textContent) as T, + filepath: filepath, + format: formatFinal, + }; + } + + if (formatFinal === primitives.FileFormats.JsonWithComments) { + return { + content: jsonc.parse(textContent) as T, + filepath: filepath, + format: formatFinal, + }; + } + + if (formatFinal === primitives.FileFormats.Yaml) { + return { + content: yaml.parse(textContent) as T, + filepath: filepath, + format: formatFinal, + }; + } + + if (formatFinal === primitives.FileFormats.Toml) { + return { + content: toml.parse(textContent) as T, + filepath: filepath, + format: formatFinal, + }; + } + + return { + content: undefined, + filepath: filepath, + format: primitives.FileFormats.Unknown, + }; +}; + +export type LoadResult = ParseResult; + +export const load = async ( + baseDir: string, + filenames: Array, + forceFormat?: primitives.FileFormat, + searchParents = false, +): Promise> => { + const filepath = await locate(baseDir, filenames, searchParents); + + if (filepath === undefined) { + return { + content: undefined, + filepath: undefined, + format: primitives.FileFormats.Unknown, + }; + } + + const result = await parse(filepath, forceFormat); + + return result; +}; diff --git a/file-loader/mod.ts b/pkg/config/file/mod.ts similarity index 100% rename from file-loader/mod.ts rename to pkg/config/file/mod.ts diff --git a/pkg/config/file/primitives.ts b/pkg/config/file/primitives.ts new file mode 100644 index 00000000..529bf8ed --- /dev/null +++ b/pkg/config/file/primitives.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +export const FileFormats = { + Unknown: 0, + EnvironmentFile: 1, + Json: 2, + JsonWithComments: 3, + Toml: 4, + Yaml: 5, +} as const; + +export type FileFormatKey = Exclude< + keyof typeof FileFormats, + number +>; +export type FileFormat = typeof FileFormats[FileFormatKey]; diff --git a/file-loader/resolver.ts b/pkg/config/file/resolver.ts similarity index 96% rename from file-loader/resolver.ts rename to pkg/config/file/resolver.ts index c89e802c..4a4d0a7f 100644 --- a/file-loader/resolver.ts +++ b/pkg/config/file/resolver.ts @@ -3,7 +3,7 @@ export const resolvePath = ( filepath: string, baseUrl: string | null = null, -) => { +): string => { if (baseUrl === null || filepath[0] === "/") { return filepath; } diff --git a/appserver/deps.ts b/pkg/config/mod.ts similarity index 51% rename from appserver/deps.ts rename to pkg/config/mod.ts index bf88a7f5..0c6f1bfc 100644 --- a/appserver/deps.ts +++ b/pkg/config/mod.ts @@ -1,3 +1,5 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -export * as semver from "jsr:@std/semver@0.220"; +export * from "./primitives.ts"; +export * from "./config.ts"; +export * from "./dotenv-source.ts"; diff --git a/pkg/config/primitives.ts b/pkg/config/primitives.ts new file mode 100644 index 00000000..cf11213a --- /dev/null +++ b/pkg/config/primitives.ts @@ -0,0 +1,53 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +export type ConfigItemMeta = { + description: string; + type: string; + ttl: number; + disallowSource: string[]; +}; + +export type ConfigItem = { + key: string; + value: T; + meta: ConfigItemMeta; +}; + +export type RequestContext = { + [key: string]: unknown; +}; + +export interface ContainerReadable { + getBooleanValue( + flagKey: string, + defaultValue: boolean, + requestContext?: RequestContext, + ): Promise; + getNumberValue( + flagKey: string, + defaultValue: boolean, + requestContext?: RequestContext, + ): Promise; + getObjectValue( + flagKey: string, + defaultValue: boolean, + requestContext?: RequestContext, + ): Promise; + getStringValue( + flagKey: string, + defaultValue: boolean, + requestContext?: RequestContext, + ): Promise; +} + +export type SourceState = { + id: string; +}; + +export type Source = { + readonly state: SourceState; +}; + +export type Provider = ContainerReadable & { + sources: Array; +}; diff --git a/di/README.md b/pkg/di/README.md similarity index 89% rename from di/README.md rename to pkg/di/README.md index 9d35097b..249b9f95 100644 --- a/di/README.md +++ b/pkg/di/README.md @@ -1,6 +1,6 @@ -# ⚙️ [cool/di](./) +# ⚙️ [@eser/di](./) -`cool/di` is a crucial component of `cool` that provides simple and efficient +`@eser/di` is a crucial component of `cool` that provides simple and efficient dependency injection solutions. ## 🚀 Getting Started with Dependency Injection (DI) @@ -19,9 +19,9 @@ instantiation and distribution of services or objects in your code. It simplifies the management of dependencies by serving as a central registry for all services. -## 🤔 What cool/di offers? +## 🤔 What @eser/di offers? -`cool/di` helps you handle these dependencies by providing a structured, +`@eser/di` helps you handle these dependencies by providing a structured, scalable and proper way to manage dependencies in your application. While its dependency injection container is focused on simplicity and efficiency, it also provides a set of decorators to make managing your application's services and @@ -29,7 +29,7 @@ dependencies even easier. ## 🛠 Usage -Here you'll find a list of features provided by `cool/di` along with brief +Here you'll find a list of features provided by `@eser/di` along with brief descriptions and usage examples. ### Registering services @@ -38,7 +38,7 @@ Start by creating a registry and registering your services using the `set`, `setLazy`, `setScoped` and `setTransient` methods. ```js -import { registry } from "$cool/di/mod.ts"; +import { registry } from "@eser/di"; // Register the mailService as a singleton service registry.set("mailService", new MailService()); @@ -61,7 +61,7 @@ registry.setLazy( Alternatively, you can chain the registration methods: ```js -import { registry } from "$cool/di/mod.ts"; +import { registry } from "@eser/di"; registry .set("mailService", new MailService()) @@ -80,7 +80,7 @@ Once you have the services container, you may retrieve your services using the literal tag to retrieve services. ```js -import { di } from "$cool/di/mod.ts"; +import { di } from "@eser/di"; // Retrieve registered services const dns = di`mailService`; @@ -92,7 +92,7 @@ const users = di`userList`; Alternatively, retrieve multiple services at once: ```js -import { services } from "$cool/di/mod.ts"; +import { services } from "@eser/di"; const [dns, mns, db, users] = di.getMany( "mailService", @@ -106,7 +106,7 @@ You can directly call your functions using the registered services as parameters: ```js -import { registry } from "$cool/di/mod.ts"; +import { registry } from "@eser/di"; di.set("serviceA", () => console.log("Service A")); di.set("serviceB", () => console.log("Service B")); @@ -126,7 +126,7 @@ template strings. This provides a more readable and concise way to access your dependencies. ```js -import { registry } from "$cool/di/mod.ts"; +import { registry } from "@eser/di"; di.set("serviceA", () => console.log("Service A")); di.set("serviceB", () => console.log("Service B")); @@ -137,7 +137,7 @@ di`first: ${"serviceA"} second: ${"serviceB"}`; // This will log "first: Service ### Decorators ```js -import { di, injectable } from "$cool/di/mod.ts"; +import { di, injectable } from "@eser/di"; @injectable() class PrinterClass { diff --git a/pkg/di/container.test.ts b/pkg/di/container.test.ts new file mode 100644 index 00000000..42b3eb5b --- /dev/null +++ b/pkg/di/container.test.ts @@ -0,0 +1,218 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import * as mock from "@std/testing/mock"; +import { Registry } from "./container.ts"; + +Deno.test("non-existent key", () => { + const registry = new Registry(); + + const container = registry.build(); + + assert.assertStrictEquals(container.get("_"), undefined); +}); + +Deno.test("symbol key", () => { + const registry = new Registry(); + const a = Symbol("a"); + + registry.set(a, 6); + + const container = registry.build(); + + assert.assertStrictEquals(container.get(a), 6); +}); + +Deno.test("mixed keys", () => { + const registry = new Registry(); + + registry.set(Object.keys, "Object.keys method"); + registry.set(Object.values, "Object.values method"); + registry.set(Object.entries, "Object.entries method"); + + const container = registry.build(); + + assert.assertStrictEquals(container.get(Object.keys), "Object.keys method"); + assert.assertStrictEquals( + container.get(Object.values), + "Object.values method", + ); + assert.assertStrictEquals( + container.get(Object.entries), + "Object.entries method", + ); +}); + +Deno.test("singleton value", () => { + const registry = new Registry(); + + registry.set("b", 5); + + const container = registry.build(); + + assert.assertStrictEquals(container.get("b"), 5); +}); + +Deno.test("singleton nullables", () => { + const registry = new Registry(); + + registry.set("c", null); + registry.set("d", undefined); + + const container = registry.build(); + + assert.assertStrictEquals(container.get("c"), null); + assert.assertStrictEquals(container.get("d"), undefined); + assert.assertStrictEquals(container.get("e", "non-exists"), "non-exists"); + assert.assertStrictEquals(container.get("f"), undefined); +}); + +Deno.test("singleton functions", () => { + const registry = new Registry(); + + registry.set("g", (x: number) => x + 3); + + const container = registry.build(); + + const result = container.get("g"); + + assert.assertStrictEquals(result.constructor, Function); + assert.assertStrictEquals(result(5), 8); +}); + +Deno.test("lazy value", () => { + const registry = new Registry(); + + const spyFn = mock.spy(); + + let number = 1; + registry.setLazy("h", () => { + spyFn(); + return ++number; + }); + + const container = registry.build(); + + assert.assertStrictEquals(number, 1); + assert.assertStrictEquals(container.get("h"), 2); + assert.assertStrictEquals(number, 2); + + mock.assertSpyCalls(spyFn, 1); +}); + +Deno.test("lazy nullable", () => { + const registry = new Registry(); + + const iSpyFn = mock.spy(); + const jSpyFn = mock.spy(); + + registry.setLazy("i", () => { + iSpyFn(); + return null; + }); + + registry.setLazy("j", () => { + jSpyFn(); + return undefined; + }); + + const container = registry.build(); + + assert.assertStrictEquals(container.get("i"), null); + assert.assertStrictEquals(container.get("j"), undefined); + + mock.assertSpyCalls(iSpyFn, 1); + mock.assertSpyCalls(jSpyFn, 1); +}); + +Deno.test("scoped value", () => { + const registry = new Registry(); + + const kSpyFn = mock.spy(); + const lSpyFn = mock.spy(); + + let number = 1; + registry.setLazy("k", () => { + kSpyFn(); + return ++number; + }); + registry.setScoped("l", () => { + lSpyFn(); + return ++number; + }); + + const container = registry.build(); + const subScope = container.createScope(); + + assert.assertStrictEquals(number, 1); + assert.assertStrictEquals(container.get("k"), 2); + assert.assertStrictEquals(container.get("k"), 2); + assert.assertStrictEquals(container.get("l"), 3); + assert.assertStrictEquals(container.get("l"), 3); + assert.assertStrictEquals(subScope.get("k"), 2); + assert.assertStrictEquals(subScope.get("k"), 2); + assert.assertStrictEquals(subScope.get("l"), 4); + assert.assertStrictEquals(subScope.get("l"), 4); + assert.assertStrictEquals(container.get("l"), 3); + assert.assertStrictEquals(number, 4); + + mock.assertSpyCalls(kSpyFn, 1); + mock.assertSpyCalls(lSpyFn, 2); +}); + +Deno.test("transient functions", () => { + const registry = new Registry(); + + let number = 1; + registry.setTransient("m", () => number++); + + const container = registry.build(); + + assert.assertStrictEquals(container.get("m"), 1); + assert.assertStrictEquals(container.get("m"), 2); + assert.assertStrictEquals(container.get("m"), 3); +}); + +Deno.test("transient promise", async () => { + const registry = new Registry(); + + registry.setTransient("n", () => Promise.resolve("test")); + + const container = registry.build(); + + assert.assertStrictEquals(await container.get("n"), "test"); +}); + +Deno.test("getMany", () => { + const registry = new Registry(); + + const lazySpyFn = mock.spy(); + const transientSpyFn = mock.spy(); + + let number = 1; + registry.set("o", number); + registry.setLazy("p", () => { + lazySpyFn(); + return ++number; + }); + registry.setTransient("q", () => { + transientSpyFn(); + return ++number; + }); + + const container = registry.build(); + + assert.assertStrictEquals(number, 1); + + const [o, p, q, r, s] = container.getMany("o", "p", "q", "q", "s"); + + assert.assertStrictEquals(number, 4); + assert.assertStrictEquals(o, 1); + assert.assertStrictEquals(p, 2); + assert.assertStrictEquals(q, 3); + assert.assertStrictEquals(r, 4); + assert.assertStrictEquals(s, undefined); + + mock.assertSpyCalls(lazySpyFn, 1); + mock.assertSpyCalls(transientSpyFn, 2); +}); diff --git a/di/container.ts b/pkg/di/container.ts similarity index 83% rename from di/container.ts rename to pkg/di/container.ts index 015be00f..173ac3e0 100644 --- a/di/container.ts +++ b/pkg/di/container.ts @@ -1,7 +1,7 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; -import { type GenericFunction } from "../standards/functions.ts"; +import * as promises from "@eser/standards/promises"; +import * as functions from "@eser/standards/functions"; import { type PromisableBuilder, type ServiceDescriptor, @@ -21,7 +21,7 @@ export class Registry ServiceDescriptor >(); - set(token: K, value: Promisable): this { + set(token: K, value: promises.Promisable): this { this.descriptors.set(token, [ServiceTypes.Singleton, value]); return this; @@ -52,16 +52,17 @@ export class Registry export class Scope implements ServiceScope { - registry: ServiceRegistry; - rootScope: ServiceScope; - items: Map> = new Map>(); + readonly registry: ServiceRegistry; + readonly rootScope: ServiceScope; + readonly items: Map>; constructor( registry: ServiceRegistry, parent?: ServiceScope, ) { this.registry = registry; - this.rootScope = parent ?? (this as ServiceScope); + this.rootScope = parent ?? this; + this.items = new Map>(); } get(token: K, defaultValue?: V2): ServiceResolution { @@ -72,7 +73,7 @@ export class Scope } if (descriptor[0] === ServiceTypes.Singleton) { - return descriptor[1] as Promisable; + return descriptor[1] as promises.Promisable; } if ( @@ -116,7 +117,10 @@ export class Scope return tokens.map((token) => this.get(token)); } - invoke(fn: T): ReturnType { + // deno-lint-ignore no-explicit-any + invoke, any>>( + fn: T, + ): ReturnType { return invoke(this, fn); } diff --git a/di/decorators.test.ts b/pkg/di/decorators.test.ts similarity index 56% rename from di/decorators.test.ts rename to pkg/di/decorators.test.ts index d699ac69..1d150b2c 100644 --- a/di/decorators.test.ts +++ b/pkg/di/decorators.test.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { assert, bdd } from "./deps-dev.ts"; +import * as assert from "@std/assert"; import { di } from "./services.ts"; import { injectable } from "./decorators.ts"; @@ -10,10 +10,8 @@ class TestClass { testValue = "testing"; } -bdd.describe("cool/di/decorators", () => { - bdd.it("decorator", () => { - const testClass = di`TestClass`; +Deno.test("decorator", () => { + const testClass = di`TestClass`; - assert.assertStrictEquals(testClass.testValue, "testing"); - }); + assert.assertStrictEquals(testClass.testValue, "testing"); }); diff --git a/di/decorators.ts b/pkg/di/decorators.ts similarity index 95% rename from di/decorators.ts rename to pkg/di/decorators.ts index 9ea5e828..2d806d7a 100644 --- a/di/decorators.ts +++ b/pkg/di/decorators.ts @@ -23,7 +23,7 @@ export const injectable = ( const name = key ?? context?.name ?? source.name; - if (name !== undefined) { + if (name !== undefined && name.length > 0) { registry.setLazy(name, () => new source()); } }; diff --git a/pkg/di/deno.jsonc b/pkg/di/deno.jsonc new file mode 100644 index 00000000..a578531e --- /dev/null +++ b/pkg/di/deno.jsonc @@ -0,0 +1,13 @@ +{ + "name": "@eser/di", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/testing": "jsr:@std/testing@^0.225.3" + }, + "exports": { + ".": "./mod.ts", + "./container": "././container.ts", + "./services": "././services.ts" + } +} diff --git a/pkg/di/fluent-api-factory.test.ts b/pkg/di/fluent-api-factory.test.ts new file mode 100644 index 00000000..aecc609b --- /dev/null +++ b/pkg/di/fluent-api-factory.test.ts @@ -0,0 +1,296 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import * as mock from "@std/testing/mock"; +import { Registry } from "./container.ts"; +import { factory } from "./fluent-api-factory.ts"; + +const create = () => { + const registry = new Registry(); + + const services = registry.build(); + + const di = factory(services); + + return { di, registry, services }; +}; + +Deno.test("di template strings: non-existent key", () => { + const { di } = create(); + + assert.assertStrictEquals(di`_`, undefined); +}); + +Deno.test("di template strings: singleton value", () => { + const { di } = create(); + + di.set("a", "value"); + + assert.assertStrictEquals(di`a`, "value"); +}); + +Deno.test("di template strings: literals", () => { + const { di } = create(); + + di.set("b", "c2"); + di.set("d", "e4"); + + assert.assertStrictEquals(di`${"b"} = ${"d"}.`, "c2 = e4."); +}); + +Deno.test("di.get(): non-string keys", () => { + const { di } = create(); + + const fnKey = () => null; + di.set(fnKey, "zz6"); + + const result = di.get(fnKey); + + assert.assertStrictEquals(result, "zz6"); +}); + +Deno.test("di.getMany()", () => { + const { di } = create(); + + di.set("f", "h3"); + di.set("g", "i5"); + + assert.assertEquals(di.getMany("f", "g"), ["h3", "i5"]); +}); + +Deno.test("di.invoke(): lambda", () => { + const { di } = create(); + + di.set("j", mock.spy()); + + // deno-fmt-ignore + const fn: (_: () => void) => void = j => j(); + + di.invoke(fn); + di.invoke(fn); + + mock.assertSpyCalls(di`j`, 2); +}); + +Deno.test("di.invoke(): lambda, multiple", () => { + const { di } = create(); + + di.set("k", mock.spy()); + di.set("l", mock.spy()); + di.set("m", mock.spy()); + + const fn = (k: () => void, l: () => void, m: () => void) => { + k(); + l(); + l(); + m(); + m(); + m(); + }; + + di.invoke(fn); + + mock.assertSpyCalls(di`k`, 1); + mock.assertSpyCalls(di`l`, 2); + mock.assertSpyCalls(di`m`, 3); +}); + +Deno.test("di.invoke(): anonymous function", () => { + const { di } = create(); + + di.set("n", mock.spy()); + + const fn = (n: () => void) => { + n(); + }; + + di.invoke(fn); + di.invoke(fn); + + mock.assertSpyCalls(di`n`, 2); +}); + +Deno.test("di.invoke(): anonymous function, multiple", () => { + const { di } = create(); + + di.set("o", mock.spy()); + di.set("p", mock.spy()); + di.set("q", mock.spy()); + + const fn = (o: () => void, p: () => void, q: () => void) => { + o(); + p(); + p(); + q(); + q(); + q(); + }; + + di.invoke(fn); + + mock.assertSpyCalls(di`o`, 1); + mock.assertSpyCalls(di`p`, 2); + mock.assertSpyCalls(di`q`, 3); +}); + +Deno.test("di.invoke(): named function", () => { + const { di } = create(); + + di.set("r", mock.spy()); + + const fn = (r: () => void) => { + r(); + }; + + di.invoke(fn); + di.invoke(fn); + + mock.assertSpyCalls(di`r`, 2); +}); + +Deno.test("di.invoke(): named function, multiple", () => { + const { di } = create(); + + di.set("s", mock.spy()); + di.set("t", mock.spy()); + di.set("u", mock.spy()); + + const fn = (s: () => void, t: () => void, u: () => void) => { + s(); + t(); + t(); + u(); + u(); + u(); + }; + + di.invoke(fn); + + mock.assertSpyCalls(di`s`, 1); + mock.assertSpyCalls(di`t`, 2); + mock.assertSpyCalls(di`u`, 3); +}); + +Deno.test("di.invoke(): generator function", () => { + const { di } = create(); + + di.set("v", mock.spy()); + + const fn = function* (v: () => void) { + yield v(); + }; + + for (const _ of di.invoke(fn)) { /* noop */ } + for (const _ of di.invoke(fn)) { /* noop */ } + + mock.assertSpyCalls(di`v`, 2); +}); + +Deno.test("di.invoke(): generator function, multiple", () => { + const { di } = create(); + + di.set("w", mock.spy()); + di.set("x", mock.spy()); + di.set("y", mock.spy()); + + const fn = function* (w: () => void, x: () => void, y: () => void) { + yield w(); + yield x(); + yield x(); + yield y(); + yield y(); + yield y(); + }; + + for (const _ of di.invoke(fn)) { /* noop */ } + + mock.assertSpyCalls(di`w`, 1); + mock.assertSpyCalls(di`x`, 2); + mock.assertSpyCalls(di`y`, 3); +}); + +Deno.test("di.invoke(): async function", async () => { + const { di } = create(); + + di.set("z", mock.spy()); + + const fn = async (z: () => void) => { + await Promise.resolve(z()); + }; + + await di.invoke(fn); + await di.invoke(fn); + + mock.assertSpyCalls(di`z`, 2); +}); + +Deno.test("di.invoke(): async function, multiple", async () => { + const { di } = create(); + + di.set("aa", mock.spy()); + di.set("ab", mock.spy()); + di.set("ac", mock.spy()); + + const fn = async ( + aa: () => void, + ab: () => void, + ac: () => void, + ) => { + await Promise.all([ + Promise.resolve(aa()), + Promise.resolve(ab()), + Promise.resolve(ab()), + Promise.resolve(ac()), + Promise.resolve(ac()), + Promise.resolve(ac()), + ]); + }; + + await di.invoke(fn); + + mock.assertSpyCalls(di`aa`, 1); + mock.assertSpyCalls(di`ab`, 2); + mock.assertSpyCalls(di`ac`, 3); +}); + +Deno.test("di.invoke(): async generator function", async () => { + const { di } = create(); + + di.set("ad", mock.spy()); + + const fn = async function* (ad: () => void) { + yield await Promise.resolve(ad()); + }; + + for await (const _ of di.invoke(fn)) { /* noop */ } + for await (const _ of di.invoke(fn)) { /* noop */ } + + mock.assertSpyCalls(di`ad`, 2); +}); + +Deno.test("di.invoke(): async generator function, multiple", async () => { + const { di } = create(); + + di.set("ae", mock.spy()); + di.set("af", mock.spy()); + di.set("ag", mock.spy()); + + const fn = async function* ( + ae: () => void, + af: () => void, + ag: () => void, + ) { + yield await Promise.resolve(ae()); + yield await Promise.resolve(af()); + yield await Promise.resolve(af()); + yield await Promise.resolve(ag()); + yield await Promise.resolve(ag()); + yield await Promise.resolve(ag()); + }; + + for await (const _ of di.invoke(fn)) { /* noop */ } + + mock.assertSpyCalls(di`ae`, 1); + mock.assertSpyCalls(di`af`, 2); + mock.assertSpyCalls(di`ag`, 3); +}); diff --git a/di/fluent-api-factory.ts b/pkg/di/fluent-api-factory.ts similarity index 80% rename from di/fluent-api-factory.ts rename to pkg/di/fluent-api-factory.ts index eb12b32c..c38634e8 100644 --- a/di/fluent-api-factory.ts +++ b/pkg/di/fluent-api-factory.ts @@ -1,7 +1,7 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; -import { type GenericFunction } from "../standards/functions.ts"; +import * as promises from "@eser/standards/promises"; +import * as functions from "@eser/standards/functions"; import { type Factory, type PromisableBuilder, @@ -38,12 +38,14 @@ export const factory = ( di.getMany = (...tokens: ReadonlyArray) => services.getMany(...tokens); - di.invoke = (fn: T): ReturnType => - services.invoke(fn); + // deno-lint-ignore no-explicit-any + di.invoke = , any>>( + fn: T, + ): ReturnType => services.invoke(fn); di.createScope = () => services.createScope(); - di.set = (token: K, value: Promisable) => + di.set = (token: K, value: promises.Promisable) => services.registry.set(token, value); di.setLazy = (token: K, value: PromisableBuilder) => diff --git a/pkg/di/invoker.test.ts b/pkg/di/invoker.test.ts new file mode 100644 index 00000000..2b37a032 --- /dev/null +++ b/pkg/di/invoker.test.ts @@ -0,0 +1,33 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { Registry } from "./container.ts"; +import { invoke } from "./invoker.ts"; + +const create = () => { + const registry = new Registry(); + + const services = registry.build(); + + return { registry, services }; +}; + +Deno.test("basic", () => { + const { registry, services } = create(); + + let count = 0; + + registry.set("singleton", "value1"); + registry.setLazy("lazy", () => "value2"); + registry.setTransient("transient", () => `value${++count + 2}`); + + const result = invoke(services, (singleton, lazy, transient) => { + return { singleton, lazy, transient }; + }); + + assert.assertEquals(result, { + singleton: "value1", + lazy: "value2", + transient: "value3", + }); +}); diff --git a/di/invoker.ts b/pkg/di/invoker.ts similarity index 86% rename from di/invoker.ts rename to pkg/di/invoker.ts index caed3aea..923dbda6 100644 --- a/di/invoker.ts +++ b/pkg/di/invoker.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type GenericFunction } from "../standards/functions.ts"; +import * as functions from "@eser/standards/functions"; import { type ServiceKey, type ServiceScope, @@ -17,7 +17,8 @@ const getFunctionParametersFromString = (fnSerialized: string) => { return []; }; -const getFunctionParameters = (fn: GenericFunction) => { +// deno-lint-ignore no-explicit-any +const getFunctionParameters = (fn: functions.GenericFunction) => { return getFunctionParametersFromString(fn.toString()); }; @@ -56,7 +57,8 @@ const getFunctionParameters = (fn: GenericFunction) => { // }; export const invoke = < - T extends GenericFunction, + // deno-lint-ignore no-explicit-any + T extends functions.GenericFunction, any>, K = ServiceKey, V = ServiceValue, >( diff --git a/di/mod.ts b/pkg/di/mod.ts similarity index 100% rename from di/mod.ts rename to pkg/di/mod.ts diff --git a/di/primitives.ts b/pkg/di/primitives.ts similarity index 74% rename from di/primitives.ts rename to pkg/di/primitives.ts index fbc625ff..37f2331e 100644 --- a/di/primitives.ts +++ b/pkg/di/primitives.ts @@ -1,10 +1,7 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; -import { - type GenericClass, - type GenericFunction, -} from "../standards/functions.ts"; +import * as promises from "@eser/standards/promises"; +import * as functions from "@eser/standards/functions"; export const ServiceTypes = { Singleton: 0, @@ -18,21 +15,23 @@ export type ServiceType = typeof ServiceTypes[ServiceTypeKey]; // deno-lint-ignore no-explicit-any export type Value = any; -export type ClassService = GenericClass; -export type FunctionService = GenericFunction; +// deno-lint-ignore no-explicit-any +export type ClassService = functions.GenericClass; +// deno-lint-ignore no-explicit-any +export type FunctionService = functions.GenericFunction; export type ServiceKey = ClassService | FunctionService | symbol | string; export type ServiceValue = ClassService | FunctionService | Value; export type PromisableBuilder = ( scope: ServiceScope, -) => Promisable; +) => promises.Promisable; export type ServiceDescriptor = readonly [ ServiceType, - Promisable | PromisableBuilder, + promises.Promisable | PromisableBuilder, ]; // tuple? export interface ServiceRegistryWritable { - set(token: K, value: Promisable): ServiceRegistry; + set(token: K, value: promises.Promisable): ServiceRegistry; setLazy(token: K, value: PromisableBuilder): ServiceRegistry; setScoped(token: K, value: PromisableBuilder): ServiceRegistry; setTransient(token: K, value: PromisableBuilder): ServiceRegistry; @@ -46,12 +45,15 @@ export type ServiceRegistry = build(): ServiceScope; }; -export type ServiceResolution = Promisable; +export type ServiceResolution = promises.Promisable; export interface ServiceScopeQueryable { get(token: K, defaultValue?: V2): ServiceResolution; getMany(...tokens: ReadonlyArray): ReadonlyArray>; - invoke(fn: T): ReturnType; + // deno-lint-ignore no-explicit-any + invoke, any>>( + fn: T, + ): ReturnType; createScope(): ServiceScope; } diff --git a/pkg/di/services.ts b/pkg/di/services.ts new file mode 100644 index 00000000..c58cbaa1 --- /dev/null +++ b/pkg/di/services.ts @@ -0,0 +1,23 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import { + type Factory, + type ServiceKey, + type ServiceRegistry, + type ServiceScope, + type ServiceValue, +} from "./primitives.ts"; +import { Registry } from "./container.ts"; +import { factory } from "./fluent-api-factory.ts"; + +export const registry: ServiceRegistry = + new Registry(); +export const services: ServiceScope = registry + .build(); + +export const di: Factory = factory< + ServiceKey, + ServiceValue +>(services); + +export { di as default }; diff --git a/directives/01-social.md b/pkg/directives/01-social.md similarity index 99% rename from directives/01-social.md rename to pkg/directives/01-social.md index 6f7fee29..3b680369 100644 --- a/directives/01-social.md +++ b/pkg/directives/01-social.md @@ -1,4 +1,4 @@ -# 📓 [cool/directives/social](./01-social.md) +# 📓 [@eser/directives/social](./01-social.md) ## 1. Purpose diff --git a/directives/02-technical.md b/pkg/directives/02-technical.md similarity index 99% rename from directives/02-technical.md rename to pkg/directives/02-technical.md index e280b98b..ce04e28a 100644 --- a/directives/02-technical.md +++ b/pkg/directives/02-technical.md @@ -1,4 +1,4 @@ -# 📓 [cool/directives/technical](./02-technical.md) +# 📓 [@eser/directives/technical](./02-technical.md) ## Technical Directives diff --git a/directives/README.md b/pkg/directives/README.md similarity index 80% rename from directives/README.md rename to pkg/directives/README.md index f9bc795b..4a58d078 100644 --- a/directives/README.md +++ b/pkg/directives/README.md @@ -1,6 +1,6 @@ -# 📓 [cool/directives](./) +# 📓 [@eser/directives](./) -`cool/directives` consists of sets of rules and recommendations that can be +`@eser/directives` consists of sets of rules and recommendations that can be applied to the entire cool ecosystem. As Captain Picard of The USS Enterprise would say, these directives are not just a set of rules, they point to a philosophy that consists of some best practices and proven experiences. diff --git a/pkg/events/container.test.ts b/pkg/events/container.test.ts new file mode 100644 index 00000000..8cfa2ea8 --- /dev/null +++ b/pkg/events/container.test.ts @@ -0,0 +1,73 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import * as mock from "@std/testing/mock"; +import { Registry } from "./container.ts"; + +Deno.test("simple", () => { + const spyFn = mock.spy(); + + const registry = new Registry(); + registry.add("ev", () => spyFn()); + + const dispatcher = registry.build(); + dispatcher.dispatch("ev"); + + mock.assertSpyCalls(spyFn, 1); +}); + +Deno.test("multiple", () => { + const spyFn = mock.spy(); + + const registry = new Registry(); + registry.add("ev", () => spyFn()); + + const dispatcher = registry.build(); + dispatcher.dispatch("ev"); + dispatcher.dispatch("ev"); + + mock.assertSpyCalls(spyFn, 2); +}); + +Deno.test("once", () => { + const spyFn = mock.spy(); + + const registry = new Registry(); + registry.add("ev", () => spyFn(), { once: true }); + + const dispatcher = registry.build(); + dispatcher.dispatch("ev"); + dispatcher.dispatch("ev"); + + mock.assertSpyCalls(spyFn, 1); +}); + +Deno.test("parameters", () => { + const spyFn = mock.spy(); + + const registry = new Registry(); + registry.add("ev", (e) => spyFn(e)); + + const dispatcher = registry.build(); + dispatcher.dispatch("ev", { detail: "xx" }); + + mock.assertSpyCalls(spyFn, 1); + + const arg = spyFn.calls[0]?.args[0] as CustomEvent; + assert.assertEquals(arg.detail, "xx"); +}); + +Deno.test("no parameters", () => { + const spyFn = mock.spy(); + + const registry = new Registry(); + registry.add("ev", (e) => spyFn(e)); + + const dispatcher = registry.build(); + dispatcher.dispatch("ev"); + + mock.assertSpyCalls(spyFn, 1); + + const arg = spyFn.calls[0]?.args[0] as CustomEvent; + assert.assertStrictEquals(arg.detail, undefined); +}); diff --git a/pkg/events/container.ts b/pkg/events/container.ts new file mode 100644 index 00000000..878d39f5 --- /dev/null +++ b/pkg/events/container.ts @@ -0,0 +1,67 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as primitives from "./primitives.ts"; + +export type RegistryState = primitives.EventRegistryState; + +export const createRegistryState = (target?: EventTarget): RegistryState => { + return { + target: target ?? new EventTarget(), + }; +}; + +export class Registry implements primitives.EventRegistry { + readonly state: RegistryState; + + constructor(state?: RegistryState) { + this.state = state ?? createRegistryState(); + } + + add( + type: string, + listener: EventListener, + options?: AddEventListenerOptions, + ): this { + this.state.target.addEventListener(type, listener, options); + + return this; + } + + remove( + type: string, + listener: EventListener, + options?: EventListenerOptions, + ): this { + this.state.target.removeEventListener(type, listener, options); + + return this; + } + + build(): primitives.EventDispatcher { + return new Dispatcher(createDispatcherState(this)); + } +} + +export type DispatcherState = primitives.EventDispatcherState; + +export const createDispatcherState = ( + registry: primitives.EventRegistry, +): DispatcherState => { + return { + registry, + }; +}; + +export class Dispatcher implements primitives.EventDispatcher { + readonly state: DispatcherState; + + constructor(state: DispatcherState) { + this.state = state; + } + + dispatch(type: string, eventInitDict?: CustomEventInit): boolean { + return this.state.registry.state.target.dispatchEvent( + new CustomEvent(type, eventInitDict), + ); + } +} diff --git a/pkg/events/deno.jsonc b/pkg/events/deno.jsonc new file mode 100644 index 00000000..9e3ba7df --- /dev/null +++ b/pkg/events/deno.jsonc @@ -0,0 +1,13 @@ +{ + "name": "@eser/events", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/testing": "jsr:@std/testing@^0.225.3" + }, + "exports": { + ".": "./mod.ts", + "./container": "././container.ts", + "./events": "././events.ts" + } +} diff --git a/events/events.ts b/pkg/events/events.ts similarity index 100% rename from events/events.ts rename to pkg/events/events.ts diff --git a/events/fluent-api-factory.ts b/pkg/events/fluent-api-factory.ts similarity index 84% rename from events/fluent-api-factory.ts rename to pkg/events/fluent-api-factory.ts index 6429fddf..9e47ea3e 100644 --- a/events/fluent-api-factory.ts +++ b/pkg/events/fluent-api-factory.ts @@ -16,13 +16,13 @@ export const factory = (dispatcher: EventDispatcher): Factory => { type: string, listener: EventListener, options?: AddEventListenerOptions, - ) => dispatcher.registry.add(type, listener, options); + ) => dispatcher.state.registry.add(type, listener, options); events.remove = ( type: string, listener: EventListener, options?: EventListenerOptions, - ) => dispatcher.registry.remove(type, listener, options); + ) => dispatcher.state.registry.remove(type, listener, options); return events; }; diff --git a/events/mod.ts b/pkg/events/mod.ts similarity index 100% rename from events/mod.ts rename to pkg/events/mod.ts diff --git a/events/primitives.ts b/pkg/events/primitives.ts similarity index 76% rename from events/primitives.ts rename to pkg/events/primitives.ts index 0c38e0f3..149824c7 100644 --- a/events/primitives.ts +++ b/pkg/events/primitives.ts @@ -13,14 +13,22 @@ export interface EventRegistryWritable { ): void; } +export type EventRegistryState = { + readonly target: EventTarget; +}; + export type EventRegistry = EventRegistryWritable & { - target: EventTarget; + readonly state: EventRegistryState; build(): EventDispatcher; }; +export type EventDispatcherState = { + readonly registry: EventRegistry; +}; + export type EventDispatcher = { - registry: EventRegistry; + readonly state: EventDispatcherState; dispatch(type: string, eventInitDict?: CustomEventInit): boolean; }; diff --git a/fp/README.md b/pkg/fp/README.md similarity index 88% rename from fp/README.md rename to pkg/fp/README.md index c1cdea66..b03eef6b 100644 --- a/fp/README.md +++ b/pkg/fp/README.md @@ -1,28 +1,28 @@ -# 🧱 [cool/fp](./) +# 🧱 [@eser/fp](./) -`cool/fp` is a library designed for functional programming enthusiasts. It +`@eser/fp` is a library designed for functional programming enthusiasts. It covers a wide range of utility functions, making it a one-stop solution for many functional programming needs. -If you're familiar with libraries like Lodash or Ramda, think of `cool/fp` as an -alternative. +If you're familiar with libraries like Lodash or Ramda, think of `@eser/fp` as +an alternative. ## 💫 Key features -- **Immutable Operations:** All methods in `cool/fp` return a new instance, +- **Immutable Operations:** All methods in `@eser/fp` return a new instance, ensuring the original data remains unchanged. (see: _no mutation_) -- **No New Data Types:** Unlike some alternatives, `cool/fp` doesn't introduce +- **No New Data Types:** Unlike some alternatives, `@eser/fp` doesn't introduce new data types or structures. - **Pure Functions:** Embrace the predictability and reliability provided by the functional programming approach, as functions always produce the same output for the same input. (see: _determinism_) -- **No Dependencies:** `cool/fp` is a standalone library with no external +- **No Dependencies:** `@eser/fp` is a standalone library with no external dependencies. -- **Type Support:** `Written in TypeScript,`cool/fp` provides full-fledged +- **Type Support:** `Written in TypeScript,`@eser/fp` provides full-fledged support for TypeScript users. ## 📚 Key functions @@ -110,7 +110,7 @@ adds new item(s) to the end of an array or generator without mutating the original. ```js -import { appendToArray } from "$cool/fp/append-to-array.ts"; +import { appendToArray } from "@eser/fp/append-to-array"; const source = ["a", "b"]; const newOne = appendToArray(source, "c"); @@ -126,7 +126,7 @@ console.log(`Is Same: ${source === newOne}`); adds new item(s) to an object without changing the original. ```js -import { appendToObject } from "$cool/fp/append-to-object.ts"; +import { appendToObject } from "@eser/fp/append-to-object"; const source = { a: 1, b: 2 }; const newOne = appendToObject(source, { c: 3 }); @@ -143,7 +143,7 @@ transforms an array or generator into an object with keys are determined by a selector function (`selectorFn`). ```js -import { associateArray } from "$cool/fp/associate-array.ts"; +import { associateArray } from "@eser/fp/associate-array"; // associate array - basic sample const categories = [ @@ -170,7 +170,7 @@ transforms an object into another object with keys are determined by a selector function (`selectorFn`). ```js -import { associateObject } from "$cool/fp/associate-object.ts"; +import { associateObject } from "@eser/fp/associate-object"; // associate object - basic sample const categories = { @@ -197,7 +197,7 @@ function as input to the next. unlike `pipe`, it executes the functions from reverse order (right to left). ```js -import { compose } from "$cool/fp/compose.ts"; +import { compose } from "@eser/fp/compose"; // compose - slug sample const lower = (x) => x.toLowerCase(); @@ -219,7 +219,7 @@ transforms a function into a curried version, allowing for partial application of arguments from the left. ```js -import { curry } from "$cool/fp/curry.ts"; +import { curry } from "@eser/fp/curry"; // curry - sum sample const sum = (a, b) => a + b; @@ -238,7 +238,7 @@ transforms a function into a curried version, allowing for partial application of arguments from the right. ```js -import { curryRight } from "$cool/fp/curry-right.ts"; +import { curryRight } from "@eser/fp/curry-right"; // curryRight - sum sample const dec = (a, b) => a - b; @@ -257,7 +257,7 @@ enhances or alters a function's behavior using a decorator function (`decoratorFn`). ```js -import { decorate } from "$cool/fp/decorate.ts"; +import { decorate } from "@eser/fp/decorate"; // decorate - calculator sample let generator = () => 5; @@ -274,14 +274,14 @@ creates a deep copy of the given data structure while preserving its constructor. ```js -import { deepCopy } from "$cool/fp/deep-copy.ts"; +import { deepCopy } from "@eser/fp/deep-copy"; -class dummy {} +class Dummy {} -const source = new dummy(); +const source = new Dummy(); const newOne = deepCopy(source); -// output: Result: class dummy {} +// output: Result: class Dummy {} console.log("Result:", newOne.constructor); // output: Is Same: false console.log(`Is Same: ${source === newOne}`); @@ -293,7 +293,7 @@ merges two data structures deeply to produce a new structure with combined items. ```js -import { deepMerge } from "$cool/fp/deep-merge.ts"; +import { deepMerge } from "@eser/fp/deep-merge"; const source = { a: { @@ -333,7 +333,7 @@ manages state mutations in a controlled manner using an initial state and a set of functions (`mutatorFns`). ```js -import { dispatcher } from "$cool/fp/dispatcher.ts"; +import { dispatcher } from "@eser/fp/dispatcher"; // dispatcher - state mutation sample const initialState = { quarter: 1, year: 2018, sum: 1 }; @@ -353,7 +353,7 @@ of functions (`mutatorFns`). additionally, it notifies subscribers about the state changes. ```js -import { dispatcher } from "$cool/fp/dispatcher.ts"; +import { dispatcher } from "@eser/fp/dispatcher"; // dispatcher - action logger sample const initialState = { quarter: 1, year: 2018, sum: 1 }; @@ -395,7 +395,7 @@ filters out duplicate values in an object based on a selector function skips the first `n` item(s) in an array or generator. ```js -import { dropFromArray } from "$cool/fp/drop-from-array.ts"; +import { dropFromArray } from "@eser/fp/drop-from-array"; const source = ["a", "b", "c"]; const newOne = dropFromArray(source, 1); @@ -411,7 +411,7 @@ console.log(`Is Same: ${source === newOne}`); Skips the first `n` item(s) in an object. ```js -import { dropFromObject } from "$cool/fp/drop-from-object.ts"; +import { dropFromObject } from "@eser/fp/drop-from-object"; const source = { a: 1, b: 2, c: 3 }; const newOne = dropFromObject(source, 1); @@ -427,7 +427,7 @@ console.log(`Is Same: ${source === newOne}`); emits events to subscribed listeners. ```js -import { emitter } from "$cool/fp/emitter.ts"; +import { emitter } from "@eser/fp/emitter"; // emitter - static pub/sub sample const subscriberOne = (value) => @@ -452,7 +452,7 @@ emits events to subscribed listeners. additionally, it notifies subscribers about the event. ```js -import { emitter } from "$cool/fp/emitter.ts"; +import { emitter } from "@eser/fp/emitter"; // emitter - event logger sample const subscriberOne = (value) => @@ -485,7 +485,7 @@ returns a new array containing only the items that satisfy the provided predicate function (`predicateFn`). ```js -import { filterArray } from "$cool/fp/filter-array.ts"; +import { filterArray } from "@eser/fp/filter-array"; const source = [1, 2, 3, 4, 5]; const newOne = filterArray(source, (x) => x <= 3); @@ -502,7 +502,7 @@ returns a new object containing only the items that satisfy the provided predicate function (`predicateFn`). ```js -import { filterObject } from "$cool/fp/filter-object.ts"; +import { filterObject } from "@eser/fp/filter-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = filterObject(source, (x) => x <= 3); @@ -519,8 +519,8 @@ iterates over an iterable (like a generator) and applies a function (`fn`) to each item. ```js -import { iterate } from "$cool/fp/iterate.ts"; -import { compose } from "$cool/fp/compose.ts"; +import { iterate } from "@eser/fp/iterate"; +import { compose } from "@eser/fp/compose"; // iterate - url fetcher example const generator = function* () { @@ -558,7 +558,7 @@ transforms each item in an array or generator using a provided predicate function (`predicateFn`). ```js -import { mapArray } from "$cool/fp/map-array.ts"; +import { mapArray } from "@eser/fp/map-array"; const source = [1, 2, 3, 4, 5]; const newOne = mapArray(source, (x) => x - 1); @@ -575,7 +575,7 @@ transforms each property in an object using a provided predicate function (`predicateFn`). ```js -import { mapObject } from "$cool/fp/map-object.ts"; +import { mapObject } from "@eser/fp/map-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = mapObject(source, (value, key) => ({ [key]: value - 1 })); @@ -591,7 +591,7 @@ console.log(`Is Same: ${source === newOne}`); combines two or more arrays into a single array. ```js -import { mergeArrays } from "$cool/fp/merge-arrays.ts"; +import { mergeArrays } from "@eser/fp/merge-arrays"; const source1 = [1, 2, 3]; const source2 = [4, 5]; @@ -608,7 +608,7 @@ console.log(`Is Same: ${source === newOne}`); combines two or more objects into a single object. ```js -import { mergeObjects } from "$cool/fp/merge-objects.ts"; +import { mergeObjects } from "@eser/fp/merge-objects"; const source1 = { a: 1, b: 2, c: 3 }; const source2 = { d: 4, e: 5 }; @@ -626,18 +626,18 @@ creates a copy of a data structure and applies a mutator function (`mutatorFn`) to it. copies an instance with its constructor, with specific mutation. ```js -import { mutate } from "$cool/fp/mutate.ts"; +import { mutate } from "@eser/fp/mutate"; -class dummy { +class Dummy { constructor() { this.items = []; } } -const source = new dummy(); +const source = new Dummy(); const newOne = mutate(source, (x) => x.items.push(6)); -// output: Result: class dummy {} +// output: Result: class Dummy {} console.log("Result:", newOne.constructor); // output: Is Same: false console.log(`Is Same: ${source === newOne}`); @@ -649,7 +649,7 @@ Returns an object containing items that match and don't match the provided criteria from an array or generator. ```js -import { pickFromArray } from "$cool/fp/pick-from-array.ts"; +import { pickFromArray } from "@eser/fp/pick-from-array"; const source = [1, 2, 3, 4, 5]; const newOne = pickFromArray(source, [2, 3, 6]); @@ -666,7 +666,7 @@ returns an object containing items that match and don't match the provided keys from an object. ```js -import { pickFromObject } from "$cool/fp/pick-from-object.ts"; +import { pickFromObject } from "@eser/fp/pick-from-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = pickFromObject(source, ["b", "c", "f"]); @@ -684,7 +684,7 @@ results of a function as an input to another one. unlike `compose`, it executes the functions from straight order (left to right). ```js -import { pipe } from "$cool/fp/pipe.ts"; +import { pipe } from "@eser/fp/pipe"; // pipe - slug sample const lower = (x) => x.toLowerCase(); @@ -706,7 +706,7 @@ adds new item(s) to the beginning of an array or generator without mutating the original. ```js -import { prependToArray } from "$cool/fp/prepend-to-array.ts"; +import { prependToArray } from "@eser/fp/prepend-to-array"; const source = ["b", "c"]; const newOne = prependToArray(source, "a"); @@ -722,7 +722,7 @@ console.log(`Is Same: ${source === newOne}`); adds new item(s) to the beginning of an object without changing the original. ```js -import { prependToObject } from "$cool/fp/prepend-to-object.ts"; +import { prependToObject } from "@eser/fp/prepend-to-object"; const source = { b: 2, c: 3 }; const newOne = prependToObject(source, { a: 1 }); @@ -739,7 +739,7 @@ removes the first item in an array or generator that matches the provided predicate function (`predicateFn`). ```js -import { removeFirstMatchFromArray } from "$cool/fp/remove-first-match-from-array.ts"; +import { removeFirstMatchFromArray } from "@eser/fp/remove-first-match-from-array"; const source = [1, 5, 2, 3, 4, 5]; const newOne = removeFirstMatchFromArray(source, (x) => x === 5); @@ -756,7 +756,7 @@ removes the first property in an object that matches the provided predicate function (`predicateFn`). ```js -import { removeFirstMatchFromObject } from "$cool/fp/remove-first-match-from-object.ts"; +import { removeFirstMatchFromObject } from "@eser/fp/remove-first-match-from-object"; const source = { a: 1, f: 5, b: 2, c: 3, d: 4, e: 5 }; const newOne = removeFirstMatchFromObject(source, (x) => x === 5); @@ -772,7 +772,7 @@ console.log(`Is Same: ${source === newOne}`); removes items from an array or generator based on their indices. ```js -import { removeIndexFromArray } from "$cool/fp/remove-index-from-array.ts"; +import { removeIndexFromArray } from "@eser/fp/remove-index-from-array"; const source = [1, 2, 3, 4, 5]; const newOne = removeIndexFromArray(source, 2, 3); @@ -788,7 +788,7 @@ console.log(`Is Same: ${source === newOne}`); removes specified values from an array or generator. ```js -import { removeValueFromArray } from "$cool/fp/remove-value-from-array.ts"; +import { removeValueFromArray } from "@eser/fp/remove-value-from-array"; const source = [1, 2, 3, 4, 5]; const newOne = removeValueFromArray(source, 2, 3); @@ -804,7 +804,7 @@ console.log(`Is Same: ${source === newOne}`); removes items from an object based on their keys. ```js -import { removeKeyFromObject } from "$cool/fp/remove-key-from-object.ts"; +import { removeKeyFromObject } from "@eser/fp/remove-key-from-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = removeKeyFromObject(source, "b", "c"); @@ -820,7 +820,7 @@ console.log(`Is Same: ${source === newOne}`); Removes items from an object based on their values. ```js -import { removeValueFromObject } from "$cool/fp/remove-value-from-object.ts"; +import { removeValueFromObject } from "@eser/fp/remove-value-from-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = removeValueFromObject(source, 2, 3); @@ -836,7 +836,7 @@ console.log(`Is Same: ${source === newOne}`); reverses the order of items in an array or generator. ```js -import { reverseArray } from "$cool/fp/reverse-array.ts"; +import { reverseArray } from "@eser/fp/reverse-array"; const source = [1, 2, 3, 4, 5]; const newOne = reverseArray(source); @@ -852,7 +852,7 @@ console.log(`Is Same: ${source === newOne}`); reverses the order of items in an object. ```js -import { reverseObject } from "$cool/fp/reverse-object.ts"; +import { reverseObject } from "@eser/fp/reverse-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = reverseObject(source); @@ -868,7 +868,7 @@ console.log(`Is Same: ${source === newOne}`); divides an array or generator into two parts based on the provided index. ```js -import { splitArray } from "$cool/fp/split-array.ts"; +import { splitArray } from "@eser/fp/split-array"; const source = [1, 2, 3, 4, 5]; const newOne = splitArray(source, 3); @@ -884,7 +884,7 @@ console.log(`Is Same: ${source === newOne}`); divides an object into two parts based on the provided index. ```js -import { splitObject } from "$cool/fp/split-object.ts"; +import { splitObject } from "@eser/fp/split-object"; const source = { a: 1, b: 2, c: 3, d: 4, e: 5 }; const newOne = splitObject(source, 3); @@ -900,7 +900,7 @@ console.log(`Is Same: ${source === newOne}`); returns the first `n` items from an array or generator. ```js -import { takeFromArray } from "$cool/fp/take-from-array.ts"; +import { takeFromArray } from "@eser/fp/take-from-array"; const source = ["a", "b", "c"]; const newOne = takeFromArray(source, 2); @@ -916,7 +916,7 @@ console.log(`Is Same: ${source === newOne}`); returns the first `n` items from an object. ```js -import { takeFromObject } from "$cool/fp/take-from-object.ts"; +import { takeFromObject } from "@eser/fp/take-from-object"; const source = { a: 1, b: 2, c: 3 }; const newOne = takeFromObject(source, 2); diff --git a/fp/append-to-array.bench.ts b/pkg/fp/append-to-array.bench.ts similarity index 66% rename from fp/append-to-array.bench.ts rename to pkg/fp/append-to-array.bench.ts index 47111c2b..5048ecbf 100644 --- a/fp/append-to-array.bench.ts +++ b/pkg/fp/append-to-array.bench.ts @@ -1,12 +1,12 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { appendToArray } from "./append-to-array.ts"; const group = "append-to-array"; -runtime.current.bench( - "cool/fp/append-to-array", +jsRuntime.current.bench( + "@eser/fp/append-to-array", { group, baseline: true }, () => { const arr1 = ["a", "b"]; @@ -15,7 +15,7 @@ runtime.current.bench( }, ); -runtime.current.bench("spread operator", { group }, () => { +jsRuntime.current.bench("spread operator", { group }, () => { const arr1 = ["a", "b"]; [...arr1, "c"]; diff --git a/pkg/fp/append-to-array.test.ts b/pkg/fp/append-to-array.test.ts new file mode 100644 index 00000000..fc41b127 --- /dev/null +++ b/pkg/fp/append-to-array.test.ts @@ -0,0 +1,31 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { appendToArray } from "./append-to-array.ts"; + +Deno.test("basic", () => { + const arr1 = ["a", "b"]; + const str1 = "c"; + + const result = appendToArray(arr1, str1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, ["a", "b", "c"]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield "a"; + yield "b"; + }; + const str1 = "c"; + + const generated1 = gen1(); + const result = appendToArray(generated1, str1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, ["a", "b", "c"]); +}); diff --git a/fp/append-to-array.ts b/pkg/fp/append-to-array.ts similarity index 100% rename from fp/append-to-array.ts rename to pkg/fp/append-to-array.ts diff --git a/fp/append-to-object.bench.ts b/pkg/fp/append-to-object.bench.ts similarity index 65% rename from fp/append-to-object.bench.ts rename to pkg/fp/append-to-object.bench.ts index 4ece85ec..dd39c2d2 100644 --- a/fp/append-to-object.bench.ts +++ b/pkg/fp/append-to-object.bench.ts @@ -1,12 +1,12 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { appendToObject } from "./append-to-object.ts"; const group = "append-to-object"; -runtime.current.bench( - "cool/fp/append-to-object", +jsRuntime.current.bench( + "@eser/fp/append-to-object", { group, baseline: true }, () => { const obj1 = { a: 1, b: 2 }; @@ -15,13 +15,13 @@ runtime.current.bench( }, ); -runtime.current.bench("Object.assign", { group }, () => { +jsRuntime.current.bench("Object.assign", { group }, () => { const obj1 = { a: 1, b: 2 }; Object.assign({}, obj1, { c: 3 }); }); -runtime.current.bench("spread operator", { group }, () => { +jsRuntime.current.bench("spread operator", { group }, () => { const obj1 = { a: 1, b: 2 }; ({ ...obj1, c: 3 }); diff --git a/pkg/fp/append-to-object.test.ts b/pkg/fp/append-to-object.test.ts new file mode 100644 index 00000000..4c9c47b6 --- /dev/null +++ b/pkg/fp/append-to-object.test.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { appendToObject } from "./append-to-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2 }; + const obj2 = { c: 3 }; + + const result = appendToObject(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertNotStrictEquals(result, obj2); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: 1, b: 2, c: 3 }); +}); diff --git a/fp/append-to-object.ts b/pkg/fp/append-to-object.ts similarity index 100% rename from fp/append-to-object.ts rename to pkg/fp/append-to-object.ts diff --git a/pkg/fp/associate-array.test.ts b/pkg/fp/associate-array.test.ts new file mode 100644 index 00000000..b2c9886d --- /dev/null +++ b/pkg/fp/associate-array.test.ts @@ -0,0 +1,44 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { associateArray } from "./associate-array.ts"; + +Deno.test("basic", () => { + const arr1 = [ + { id: 1, name: "foo" }, + { id: 2, name: "bar" }, + { id: 3, name: "baz" }, + ]; + const func1 = (value: { id: number }) => value.id; + + const result = associateArray(arr1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, arr1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { + 1: { id: 1, name: "foo" }, + 2: { id: 2, name: "bar" }, + 3: { id: 3, name: "baz" }, + }); +}); + +Deno.test("with-value-skipping", () => { + const arr1 = [ + { id: 1, name: "foo", skip: false }, + { id: 2, name: "bar", skip: false }, + { id: 3, name: "baz", skip: true }, + ]; + const func1 = (value: { id: number; skip: boolean }) => + value.skip ? undefined : value.id; + + const result = associateArray(arr1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, arr1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { + 1: { id: 1, name: "foo", skip: false }, + 2: { id: 2, name: "bar", skip: false }, + }); +}); diff --git a/fp/associate-array.ts b/pkg/fp/associate-array.ts similarity index 100% rename from fp/associate-array.ts rename to pkg/fp/associate-array.ts diff --git a/pkg/fp/associate-object.test.ts b/pkg/fp/associate-object.test.ts new file mode 100644 index 00000000..11273730 --- /dev/null +++ b/pkg/fp/associate-object.test.ts @@ -0,0 +1,42 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { associateObject } from "./associate-object.ts"; + +Deno.test("basic", () => { + const obj1 = { + a: { id: 1, name: "foo" }, + b: { id: 2, name: "bar" }, + c: { id: 3, name: "baz" }, + }; + const func1 = (value: { id: number }) => value.id; + + const result = associateObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { + 1: { id: 1, name: "foo" }, + 2: { id: 2, name: "bar" }, + 3: { id: 3, name: "baz" }, + }); +}); + +Deno.test("with-value-skipping", () => { + const obj1 = { + a: { id: 1, name: "foo", skip: false }, + b: { id: 2, name: "bar", skip: false }, + c: { id: 3, name: "baz", skip: true }, + }; + const func1 = (value: { id: number; skip: boolean }) => + value.skip ? undefined : value.id; + + const result = associateObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { + 1: { id: 1, name: "foo", skip: false }, + 2: { id: 2, name: "bar", skip: false }, + }); +}); diff --git a/fp/associate-object.ts b/pkg/fp/associate-object.ts similarity index 100% rename from fp/associate-object.ts rename to pkg/fp/associate-object.ts diff --git a/pkg/fp/compose.test.ts b/pkg/fp/compose.test.ts new file mode 100644 index 00000000..16693f56 --- /dev/null +++ b/pkg/fp/compose.test.ts @@ -0,0 +1,17 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { compose } from "./compose.ts"; + +Deno.test("basic", () => { + const lower = (x: string) => x.toLowerCase(); + const chars = (x: string) => x.replace(/[^\w \\-]+/g, ""); + const spaces = (x: string) => x.split(" "); + const dashes = (x: Array) => x.join("-"); + + const slug = compose(dashes, spaces, chars, lower); + + const result = slug("Hello World!"); + + assert.assertEquals(result, "hello-world"); +}); diff --git a/pkg/fp/compose.ts b/pkg/fp/compose.ts new file mode 100644 index 00000000..6fc8b5d6 --- /dev/null +++ b/pkg/fp/compose.ts @@ -0,0 +1,19 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as functions from "@eser/standards/functions"; + +// deno-lint-ignore no-explicit-any +type ComposableFunction = functions.GenericFunction; + +export const compose = ( + ...funcs: functions.ArgList +): ComposableFunction => { + return funcs.reduce( + (previousFunction, currentFunction) => + // deno-lint-ignore no-explicit-any + (...args: functions.ArgList) => + previousFunction(currentFunction(...args)), + ); +}; + +export { compose as default }; diff --git a/pkg/fp/curry-right.test.ts b/pkg/fp/curry-right.test.ts new file mode 100644 index 00000000..f914d832 --- /dev/null +++ b/pkg/fp/curry-right.test.ts @@ -0,0 +1,14 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { curryRight } from "./curry-right.ts"; + +Deno.test("basic", () => { + const dec = (a: number, b: number) => a - b; + + const decWith5 = curryRight(dec, 5); + + const result = decWith5(3); + + assert.assertEquals(result, -2); +}); diff --git a/pkg/fp/curry-right.ts b/pkg/fp/curry-right.ts new file mode 100644 index 00000000..6d7b4700 --- /dev/null +++ b/pkg/fp/curry-right.ts @@ -0,0 +1,18 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as functions from "@eser/standards/functions"; +import * as utilities from "@eser/standards/utilities"; + +export const curryRight = < + // deno-lint-ignore no-explicit-any + TF extends functions.GenericFunction, + TP extends Partial>, +>(func: TF, ...argsOnRight: TP): ( + ...argsOnLeft: utilities.RemainingParametersRight, TP> +) => ReturnType => { + return ( + ...argsOnLeft: utilities.RemainingParametersRight, TP> + ) => func(...argsOnLeft, ...argsOnRight); +}; + +export { curryRight as default }; diff --git a/pkg/fp/curry.test.ts b/pkg/fp/curry.test.ts new file mode 100644 index 00000000..7efd4702 --- /dev/null +++ b/pkg/fp/curry.test.ts @@ -0,0 +1,14 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { curry } from "./curry.ts"; + +Deno.test("basic", () => { + const sum = (a: number, b: number) => a + b; + + const sumWith5 = curry(sum, 5); + + const result = sumWith5(3); + + assert.assertEquals(result, 8); +}); diff --git a/pkg/fp/curry.ts b/pkg/fp/curry.ts new file mode 100644 index 00000000..6b3cac34 --- /dev/null +++ b/pkg/fp/curry.ts @@ -0,0 +1,17 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as functions from "@eser/standards/functions"; +import * as utilities from "@eser/standards/utilities"; + +export const curry = < + // deno-lint-ignore no-explicit-any + TF extends functions.GenericFunction, + TP extends Partial>, +>(func: TF, ...argsOnLeft: TP): ( + ...argsOnRight: utilities.RemainingParameters, TP> +) => ReturnType => { + return (...argsOnRight: utilities.RemainingParameters, TP>) => + func(...argsOnLeft, ...argsOnRight); +}; + +export { curry as default }; diff --git a/pkg/fp/decorate.test.ts b/pkg/fp/decorate.test.ts new file mode 100644 index 00000000..aa545b86 --- /dev/null +++ b/pkg/fp/decorate.test.ts @@ -0,0 +1,26 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { decorate } from "./decorate.ts"; + +Deno.test("basic", () => { + let generator = () => 5; + + generator = decorate(generator, (x) => x() * 2); + generator = decorate(generator, (x) => x() + 1); + + const result = generator(); + + assert.assertEquals(result, 11); +}); + +Deno.test("parameters", () => { + let generator = (a: number) => a + 5; + + generator = decorate(generator, (x, a) => x(a) * 2); + generator = decorate(generator, (x, a) => x(a) + 1); + + const result = generator(3); + + assert.assertEquals(result, 17); +}); diff --git a/fp/decorate.ts b/pkg/fp/decorate.ts similarity index 100% rename from fp/decorate.ts rename to pkg/fp/decorate.ts diff --git a/fp/deep-copy.bench.ts b/pkg/fp/deep-copy.bench.ts similarity index 64% rename from fp/deep-copy.bench.ts rename to pkg/fp/deep-copy.bench.ts index 6a69924e..e66129bf 100644 --- a/fp/deep-copy.bench.ts +++ b/pkg/fp/deep-copy.bench.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { deepCopy, deepCopy2 } from "./deep-copy.ts"; const group = "deep-copy"; @@ -13,19 +13,19 @@ class Dummy { } } -runtime.current.bench("cool/fp/deep-copy", { group, baseline: true }, () => { +jsRuntime.current.bench("@eser/fp/deep-copy", { group, baseline: true }, () => { const obj1 = new Dummy({ value: 5 }); deepCopy(obj1); }); -runtime.current.bench("cool/fp/deep-copy-2", { group }, () => { +jsRuntime.current.bench("@eser/fp/deep-copy-2", { group }, () => { const obj1 = new Dummy({ value: 5 }); deepCopy2(obj1); }); -runtime.current.bench("structuredClone", { group }, () => { +jsRuntime.current.bench("structuredClone", { group }, () => { const obj1 = new Dummy({ value: 5 }); structuredClone(obj1); diff --git a/pkg/fp/deep-copy.test.ts b/pkg/fp/deep-copy.test.ts new file mode 100644 index 00000000..6b204aca --- /dev/null +++ b/pkg/fp/deep-copy.test.ts @@ -0,0 +1,34 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { deepCopy } from "./deep-copy.ts"; + +class Dummy { + prop: unknown; + + constructor(prop: unknown) { + this.prop = prop; + } +} + +Deno.test("basic", () => { + const obj1 = { value: 5 }; + + const result = deepCopy(obj1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertStrictEquals(result.constructor, Object); + assert.assertEquals(result, obj1); + assert.assertEquals(result, { value: 5 }); +}); + +Deno.test("classes", () => { + const obj1 = new Dummy({ value: 5 }); + + const result = deepCopy(obj1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertStrictEquals(result.constructor, Dummy); + assert.assertEquals(result, obj1); + assert.assertEquals(result, new Dummy({ value: 5 })); +}); diff --git a/fp/deep-copy.ts b/pkg/fp/deep-copy.ts similarity index 100% rename from fp/deep-copy.ts rename to pkg/fp/deep-copy.ts diff --git a/pkg/fp/deep-merge.test.ts b/pkg/fp/deep-merge.test.ts new file mode 100644 index 00000000..edf9217e --- /dev/null +++ b/pkg/fp/deep-merge.test.ts @@ -0,0 +1,104 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { deepMerge } from "./deep-merge.ts"; + +type Dummy1Prop = { + inners: { + inner1: number; + inner2: Array; + inner3?: number; + }; + outer?: string; +}; + +class Dummy1 { + prop: Dummy1Prop; + + constructor(prop: Dummy1Prop) { + this.prop = prop; + } +} + +type Dummy2Prop = { + inners: { + inner2: Array; + inner3: number; + }; + outer: string; +}; + +class Dummy2 { + prop: Dummy2Prop; + + constructor(prop: Dummy2Prop) { + this.prop = prop; + } +} + +Deno.test("basic", () => { + const obj1 = { + a: { + b: [1, 2, 3], + c: { + d: 4, + }, + }, + }; + + const obj2 = { + a: { + b: [55], + }, + e: "hello", + }; + + const result = deepMerge(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertNotStrictEquals(result, obj2); + assert.assertStrictEquals(result.constructor, Object); + assert.assertEquals(result, { + a: { + b: [55], + c: { + d: 4, + }, + }, + e: "hello", + }); +}); + +Deno.test("classes", () => { + const obj1 = new Dummy1({ + inners: { + inner1: 1, + inner2: [2, 3], + }, + }); + + const obj2 = new Dummy2({ + inners: { + inner2: [4, 5], + inner3: 6, + }, + outer: "sub-mariner", + }); + + const result = deepMerge(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertNotStrictEquals(result, obj2); + assert.assertStrictEquals(result.constructor, Dummy1); + assert.assertEquals( + result, + new Dummy1({ + inners: { + inner1: 1, + inner2: [4, 5], + inner3: 6, + }, + outer: "sub-mariner", + }), + ); +}); diff --git a/fp/deep-merge.ts b/pkg/fp/deep-merge.ts similarity index 100% rename from fp/deep-merge.ts rename to pkg/fp/deep-merge.ts diff --git a/pkg/fp/deno.jsonc b/pkg/fp/deno.jsonc new file mode 100644 index 00000000..b6538f62 --- /dev/null +++ b/pkg/fp/deno.jsonc @@ -0,0 +1,54 @@ +{ + "name": "@eser/fp", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0" + }, + "exports": { + ".": "./mod.ts", + "./append-to-array": "./append-to-array.ts", + "./append-to-object": "./append-to-object.ts", + "./associate-array": "./associate-array.ts", + "./associate-object": "./associate-object.ts", + "./compose": "./compose.ts", + "./curry-right": "./curry-right.ts", + "./curry": "./curry.ts", + "./decorate": "./decorate.ts", + "./deep-copy": "./deep-copy.ts", + "./deep-merge": "./deep-merge.ts", + "./dispatcher": "./dispatcher.ts", + "./distinct-array": "./distinct-array.ts", + "./distinct-object": "./distinct-object.ts", + "./drop-from-array": "./drop-from-array.ts", + "./drop-from-object": "./drop-from-object.ts", + "./emitter": "./emitter.ts", + "./filter-array": "./filter-array.ts", + "./filter-object": "./filter-object.ts", + "./iterate": "./iterate.ts", + "./map-array": "./map-array.ts", + "./map-object": "./map-object.ts", + "./match": "./match.ts", + "./merge-arrays": "./merge-arrays.ts", + "./merge-objects": "./merge-objects.ts", + "./mutate": "./mutate.ts", + "./pick-from-array": "./pick-from-array.ts", + "./pick-from-object": "./pick-from-object.ts", + "./pipe": "./pipe.ts", + "./prepend-to-array": "./prepend-to-array.ts", + "./prepend-to-object": "./prepend-to-object.ts", + "./remove-first-match-from-array": "./remove-first-match-from-array.ts", + "./remove-first-match-from-object": "./remove-first-match-from-object.ts", + "./remove-index-from-array": "./remove-index-from-array.ts", + "./remove-key-from-object": "./remove-key-from-object.ts", + "./remove-value-from-array": "./remove-value-from-array.ts", + "./remove-value-from-object": "./remove-value-from-object.ts", + "./reverse-array": "./reverse-array.ts", + "./reverse-object": "./reverse-object.ts", + "./split-array": "./split-array.ts", + "./split-object": "./split-object.ts", + "./take-from-array": "./take-from-array.ts", + "./take-from-object": "./take-from-object.ts", + "./wth": "./wth.ts", + "./wthout": "./wthout.ts" + } +} diff --git a/pkg/fp/dispatcher.test.ts b/pkg/fp/dispatcher.test.ts new file mode 100644 index 00000000..69430671 --- /dev/null +++ b/pkg/fp/dispatcher.test.ts @@ -0,0 +1,64 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { dispatcher, type LogType, type NextType } from "./dispatcher.ts"; + +type StateType = Record; + +Deno.test("basic", async () => { + const initialState = { quarter: 1, year: 2018, sum: 1 }; + + const actionAdd5 = (state: StateType, next: NextType) => + next({ ...state, sum: (state["sum"] ?? 0) + 5 }); + const actionDiv2 = (state: StateType, next: NextType) => + next({ ...state, sum: (state["sum"] ?? 0) / 2 }); + + const result = await dispatcher(initialState, [actionAdd5, actionDiv2]); + + assert.assertEquals(result, { quarter: 1, year: 2018, sum: 3 }); +}); + +Deno.test("logger", async () => { + const initialState = { quarter: 1, year: 2018, sum: 1 }; + + const actionAdd5 = (state: StateType, next: NextType) => + next({ ...state, sum: (state["sum"] ?? 0) + 5 }); + const actionDiv2 = (state: StateType, next: NextType) => + next({ ...state, sum: (state["sum"] ?? 0) / 2 }); + + const logs: Array> = []; + const logger = (entry: LogType) => logs.push(entry); + + const result = await dispatcher(initialState, [actionAdd5, actionDiv2], [ + logger, + ]); + + assert.assertEquals(result, { quarter: 1, year: 2018, sum: 3 }); + assert.assertEquals(logs, [ + { + action: "actionAdd5", + previousState: { quarter: 1, year: 2018, sum: 1 }, + newState: { quarter: 1, year: 2018, sum: 6 }, + }, + { + action: "actionDiv2", + previousState: { quarter: 1, year: 2018, sum: 6 }, + newState: { quarter: 1, year: 2018, sum: 3 }, + }, + ]); +}); + +Deno.test("promises", async () => { + const initialState = 0; + + const delay = (num: number) => + new Promise((resolve) => setTimeout(() => resolve(num), 200)); + const actionFirst = async (state: number, next: NextType) => + next(state + await delay(5)); + const actionSecond = async (state: number, next: NextType) => + next(state + await delay(3)); + + const result = await dispatcher(initialState, [actionFirst, actionSecond]); + + assert.assertEquals(result, 8); +}); diff --git a/fp/dispatcher.ts b/pkg/fp/dispatcher.ts similarity index 100% rename from fp/dispatcher.ts rename to pkg/fp/dispatcher.ts diff --git a/pkg/fp/distinct-array.test.ts b/pkg/fp/distinct-array.test.ts new file mode 100644 index 00000000..3d3eb83e --- /dev/null +++ b/pkg/fp/distinct-array.test.ts @@ -0,0 +1,22 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { distinctArray } from "./distinct-array.ts"; + +Deno.test("basic", () => { + const arr1 = [ + { id: 1, name: "foo", parent: 0 }, + { id: 2, name: "bar", parent: 1 }, + { id: 3, name: "baz", parent: 1 }, + ]; + const func1 = (item: { parent: number }) => item.parent; + + const result = distinctArray(arr1, func1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, [ + { id: 1, name: "foo", parent: 0 }, + { id: 2, name: "bar", parent: 1 }, + ]); +}); diff --git a/fp/distinct-array.ts b/pkg/fp/distinct-array.ts similarity index 100% rename from fp/distinct-array.ts rename to pkg/fp/distinct-array.ts diff --git a/pkg/fp/distinct-object.test.ts b/pkg/fp/distinct-object.test.ts new file mode 100644 index 00000000..f4b925d1 --- /dev/null +++ b/pkg/fp/distinct-object.test.ts @@ -0,0 +1,22 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { distinctObject } from "./distinct-object.ts"; + +Deno.test("basic", () => { + const obj1 = { + a: { id: 1, name: "foo", parent: 0 }, + b: { id: 2, name: "bar", parent: 1 }, + c: { id: 3, name: "baz", parent: 1 }, + }; + const func1 = (item: { parent: number }) => item.parent; + + const result = distinctObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { + a: { id: 1, name: "foo", parent: 0 }, + b: { id: 2, name: "bar", parent: 1 }, + }); +}); diff --git a/fp/distinct-object.ts b/pkg/fp/distinct-object.ts similarity index 100% rename from fp/distinct-object.ts rename to pkg/fp/distinct-object.ts diff --git a/pkg/fp/drop-from-array.test.ts b/pkg/fp/drop-from-array.test.ts new file mode 100644 index 00000000..84b24f90 --- /dev/null +++ b/pkg/fp/drop-from-array.test.ts @@ -0,0 +1,32 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { dropFromArray } from "./drop-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = ["a", "b", "c"]; + const int1 = 1; + + const result = dropFromArray(arr1, int1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 2); + assert.assertEquals(result, ["b", "c"]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield "a"; + yield "b"; + yield "c"; + }; + const int1 = 1; + + const generated1 = gen1(); + const result = dropFromArray(generated1, int1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 2); + assert.assertEquals(result, ["b", "c"]); +}); diff --git a/fp/drop-from-array.ts b/pkg/fp/drop-from-array.ts similarity index 100% rename from fp/drop-from-array.ts rename to pkg/fp/drop-from-array.ts diff --git a/pkg/fp/drop-from-object.test.ts b/pkg/fp/drop-from-object.test.ts new file mode 100644 index 00000000..5abe00da --- /dev/null +++ b/pkg/fp/drop-from-object.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { dropFromObject } from "./drop-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3 }; + const int1 = 1; + + const result = dropFromObject(obj1, int1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { b: 2, c: 3 }); +}); diff --git a/fp/drop-from-object.ts b/pkg/fp/drop-from-object.ts similarity index 100% rename from fp/drop-from-object.ts rename to pkg/fp/drop-from-object.ts diff --git a/pkg/fp/emitter.test.ts b/pkg/fp/emitter.test.ts new file mode 100644 index 00000000..6e42615b --- /dev/null +++ b/pkg/fp/emitter.test.ts @@ -0,0 +1,120 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { emitter, type LogType } from "./emitter.ts"; + +Deno.test("basic", async () => { + let sideEffectCounter = 0; + + const subscriberOne = () => { + sideEffectCounter += 1; + }; + const subscriberTwo = () => { + sideEffectCounter += 2; + }; + + const events = { + calculate: [subscriberOne, subscriberTwo], + }; + + await emitter(events, "calculate"); + + assert.assertEquals(sideEffectCounter, 3); +}); + +Deno.test("many events", async () => { + let sideEffectCounter = 0; + + const subscriberAddOne = () => { + sideEffectCounter += 1; + }; + const subscriberAddTwo = () => { + sideEffectCounter += 2; + }; + const subscriberSubOne = () => { + sideEffectCounter -= 1; + }; + + const events = { + add: [subscriberAddOne, subscriberAddTwo], + sub: [subscriberSubOne], + }; + + await emitter(events, "add"); + await emitter(events, "sub"); + + assert.assertEquals(sideEffectCounter, 2); +}); + +Deno.test("wildcard events", async () => { + let sideEffectCounter = 0; + + const subscriberOne = () => { + sideEffectCounter += 1; + }; + const subscriberTwo = () => { + sideEffectCounter -= 2; + }; + + const events = { + inc: [subscriberOne], + dec: [subscriberTwo], + }; + + await emitter(events, "*"); + + assert.assertEquals(sideEffectCounter, -1); +}); + +Deno.test("parameters", async () => { + let sideEffectCounter = 0; + + const subscriberOne = (value: number) => { + sideEffectCounter += value; + }; + const subscriberTwo = (value: number) => { + sideEffectCounter += value * 2; + }; + + const events = { + calculate: [subscriberOne, subscriberTwo], + }; + + await emitter(events, "calculate", [5]); + + assert.assertEquals(sideEffectCounter, 15); +}); + +Deno.test("subscribers", async () => { + let sideEffectCounter = 0; + + const subscriberOne = (value: number) => { + sideEffectCounter += value; + }; + const subscriberTwo = (value: number) => { + sideEffectCounter += value * 2; + }; + + const events = { + calculate: [subscriberOne, subscriberTwo], + }; + + const logs: Array = []; + const logger = (entry: LogType) => logs.push(entry); + + await emitter(events, "calculate", [5], [logger]); + + assert.assertEquals(sideEffectCounter, 15); + assert.assertEquals(logs, [ + { + event: "calculate", + subscriber: "subscriberOne", + args: [5], + }, + { + event: "calculate", + subscriber: "subscriberTwo", + args: [5], + }, + ]); +}); diff --git a/fp/emitter.ts b/pkg/fp/emitter.ts similarity index 80% rename from fp/emitter.ts rename to pkg/fp/emitter.ts index 83b082f7..2b438a7c 100644 --- a/fp/emitter.ts +++ b/pkg/fp/emitter.ts @@ -1,9 +1,12 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; -import { type ArgList } from "../standards/functions.ts"; +import * as promises from "@eser/standards/promises"; +import * as functions from "@eser/standards/functions"; -export type EventType = (...args: ArgList) => Promisable; +export type EventType = ( + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList +) => promises.Promisable; export type LogType = { event: string; diff --git a/pkg/fp/filter-array.test.ts b/pkg/fp/filter-array.test.ts new file mode 100644 index 00000000..9d98f819 --- /dev/null +++ b/pkg/fp/filter-array.test.ts @@ -0,0 +1,34 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { filterArray } from "./filter-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const func1 = (x: number) => x <= 3; + + const result = filterArray(arr1, func1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 2, 3]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + const func1 = (x: number) => x <= 3; + + const generated1 = gen1(); + const result = filterArray(generated1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 2, 3]); +}); diff --git a/fp/filter-array.ts b/pkg/fp/filter-array.ts similarity index 100% rename from fp/filter-array.ts rename to pkg/fp/filter-array.ts diff --git a/pkg/fp/filter-object.test.ts b/pkg/fp/filter-object.test.ts new file mode 100644 index 00000000..57a90f8d --- /dev/null +++ b/pkg/fp/filter-object.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { filterObject } from "./filter-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const func1 = (x: number) => x <= 3; + + const result = filterObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: 1, b: 2, c: 3 }); +}); diff --git a/fp/filter-object.ts b/pkg/fp/filter-object.ts similarity index 100% rename from fp/filter-object.ts rename to pkg/fp/filter-object.ts diff --git a/pkg/fp/iterate.test.ts b/pkg/fp/iterate.test.ts new file mode 100644 index 00000000..f733afdf --- /dev/null +++ b/pkg/fp/iterate.test.ts @@ -0,0 +1,76 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { pipe } from "./pipe.ts"; +import { iterate } from "./iterate.ts"; + +Deno.test("basic", async () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + }; + + let total = 0; + + const func1 = (x: number) => { + total += x; + }; + + await iterate(gen1(), func1); + + assert.assertEquals(total, 6); +}); + +Deno.test("async", async () => { + const delay = (ms: number, value: number): Promise => + new Promise((resolve, _reject) => { + setTimeout( + () => resolve(value), + ms, + ); + }); + + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + }; + + let total = 0; + + const func1 = async (x: number) => { + total += await delay(10, x); + }; + + await iterate(gen1(), func1); + + assert.assertEquals(total, 6); +}); + +Deno.test("pipe", async () => { + const gen1 = function* () { + yield { value: 1 }; + yield { value: 2 }; + yield { value: 3 }; + }; + + let total = 0; + + const getValue = (x: { value: number }) => Promise.resolve(x.value); + const add5 = async (value: Promise) => await value + 5; + const sumWithTotal = async (value: Promise) => { + total += await value; + }; + + await iterate( + gen1(), + pipe( + getValue, + add5, + sumWithTotal, + ), + ); + + assert.assertEquals(total, 21); +}); diff --git a/fp/iterate.ts b/pkg/fp/iterate.ts similarity index 59% rename from fp/iterate.ts rename to pkg/fp/iterate.ts index 3fb3e0d0..8967bacd 100644 --- a/fp/iterate.ts +++ b/pkg/fp/iterate.ts @@ -1,13 +1,13 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type Promisable } from "../standards/promises.ts"; -import { type ArgList } from "../standards/functions.ts"; +import * as promises from "@eser/standards/promises"; +import * as functions from "@eser/standards/functions"; export const iterate = async ( // deno-lint-ignore no-explicit-any - iterable: Promisable>, + iterable: promises.Promisable>, // deno-lint-ignore no-explicit-any - func: (...args: ArgList) => Promisable, + func: (...args: functions.ArgList) => promises.Promisable, ): Promise => { for (const value of await iterable) { await func(value); diff --git a/pkg/fp/map-array.test.ts b/pkg/fp/map-array.test.ts new file mode 100644 index 00000000..534e2cb2 --- /dev/null +++ b/pkg/fp/map-array.test.ts @@ -0,0 +1,34 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { mapArray } from "./map-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const func1 = (x: number) => x - 1; + + const result = mapArray(arr1, func1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [0, 1, 2, 3, 4]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + const func1 = (x: number) => x - 1; + + const generated1 = gen1(); + const result = mapArray(generated1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [0, 1, 2, 3, 4]); +}); diff --git a/fp/map-array.ts b/pkg/fp/map-array.ts similarity index 100% rename from fp/map-array.ts rename to pkg/fp/map-array.ts diff --git a/pkg/fp/map-object.test.ts b/pkg/fp/map-object.test.ts new file mode 100644 index 00000000..97d6018d --- /dev/null +++ b/pkg/fp/map-object.test.ts @@ -0,0 +1,38 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { mapObject } from "./map-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const func1 = (value: number, key: string | number | symbol) => ({ + [key]: value - 1, + }); + + const result = mapObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 5); + assert.assertEquals(result, { a: 0, b: 1, c: 2, d: 3, e: 4 }); +}); + +Deno.test("with-value-skipping", () => { + const obj1 = { a: 1, b: 2, c: null }; + const func1 = ( + value: number | null, + key: string | number | symbol, + ) => { + if (value === null) { + return null; + } + + return { [key]: value - 1 }; + }; + + const result = mapObject(obj1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, obj1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { a: 0, b: 1 }); +}); diff --git a/fp/map-object.ts b/pkg/fp/map-object.ts similarity index 100% rename from fp/map-object.ts rename to pkg/fp/map-object.ts diff --git a/pkg/fp/match.test.ts b/pkg/fp/match.test.ts new file mode 100644 index 00000000..044b2e86 --- /dev/null +++ b/pkg/fp/match.test.ts @@ -0,0 +1,37 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { match } from "./match.ts"; + +Deno.test("basic", () => { + const str1 = "apple"; + + const result = match(str1, [ + ["apple", () => "Apple is selected."], + ["pear", () => "Pear is selected."], + ["banana", () => "Banana is selected."], + ]); + + assert.assertEquals( + result, + "Apple is selected.", + ); +}); + +Deno.test("conditions", () => { + const str1 = "appLe"; // intentionally mixed case + const str2 = "pear"; + const str3 = "banana"; + + const result = match(true, [ + // @ts-ignore - intentionally testing for falsey values + [str1 === "apple", () => "Apple is selected."], + [str2 === "pear", () => "Pear is selected."], + [str3 === "banana", () => "Banana is selected."], + ]); + + assert.assertEquals( + result, + "Pear is selected.", + ); +}); diff --git a/fp/match.ts b/pkg/fp/match.ts similarity index 100% rename from fp/match.ts rename to pkg/fp/match.ts diff --git a/pkg/fp/merge-arrays.test.ts b/pkg/fp/merge-arrays.test.ts new file mode 100644 index 00000000..98420656 --- /dev/null +++ b/pkg/fp/merge-arrays.test.ts @@ -0,0 +1,51 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { mergeArrays } from "./merge-arrays.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3]; + const arr2 = [4, 5]; + + const result = mergeArrays(arr1, arr2); + + assert.assertNotStrictEquals(result, arr1); + assert.assertNotStrictEquals(result, arr2); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [1, 2, 3, 4, 5]); +}); + +Deno.test("with-generator-1", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + }; + const arr1 = [4, 5]; + + const generated1 = gen1(); + const result = mergeArrays(generated1, arr1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [1, 2, 3, 4, 5]); +}); + +Deno.test("with-generator-2", () => { + const arr1 = [1, 2, 3]; + const gen1 = function* () { + yield 4; + yield 5; + }; + + const generated1 = gen1(); + const result = mergeArrays(arr1, generated1); + + assert.assertNotStrictEquals(result, arr1); + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [1, 2, 3, 4, 5]); +}); diff --git a/fp/merge-arrays.ts b/pkg/fp/merge-arrays.ts similarity index 100% rename from fp/merge-arrays.ts rename to pkg/fp/merge-arrays.ts diff --git a/pkg/fp/merge-objects.test.ts b/pkg/fp/merge-objects.test.ts new file mode 100644 index 00000000..1046d11f --- /dev/null +++ b/pkg/fp/merge-objects.test.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { mergeObjects } from "./merge-objects.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2 }; + const obj2 = { c: 3 }; + + const result = mergeObjects(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertNotStrictEquals(result, obj2); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: 1, b: 2, c: 3 }); +}); diff --git a/fp/merge-objects.ts b/pkg/fp/merge-objects.ts similarity index 100% rename from fp/merge-objects.ts rename to pkg/fp/merge-objects.ts diff --git a/fp/mod.ts b/pkg/fp/mod.ts similarity index 100% rename from fp/mod.ts rename to pkg/fp/mod.ts diff --git a/fp/mutate.bench.ts b/pkg/fp/mutate.bench.ts similarity index 69% rename from fp/mutate.bench.ts rename to pkg/fp/mutate.bench.ts index 5afec332..eb2d789b 100644 --- a/fp/mutate.bench.ts +++ b/pkg/fp/mutate.bench.ts @@ -1,11 +1,11 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { mutate } from "./mutate.ts"; const group = "mutate"; -runtime.current.bench("cool/fp/mutate", { group, baseline: true }, () => { +jsRuntime.current.bench("@eser/fp/mutate", { group, baseline: true }, () => { const obj1 = { firstName: "Eser", lastName: "Ozvataf", @@ -15,7 +15,7 @@ runtime.current.bench("cool/fp/mutate", { group, baseline: true }, () => { mutate(obj1, (x) => x.firstName = "Helo"); }); -runtime.current.bench("Object.assign", { group }, () => { +jsRuntime.current.bench("Object.assign", { group }, () => { const obj1 = { firstName: "Eser", lastName: "Ozvataf", @@ -25,7 +25,7 @@ runtime.current.bench("Object.assign", { group }, () => { Object.assign({}, obj1, { firstName: "Helo" }); }); -runtime.current.bench("spread operator", { group }, () => { +jsRuntime.current.bench("spread operator", { group }, () => { const obj1 = { firstName: "Eser", lastName: "Ozvataf", diff --git a/pkg/fp/mutate.test.ts b/pkg/fp/mutate.test.ts new file mode 100644 index 00000000..13a3581e --- /dev/null +++ b/pkg/fp/mutate.test.ts @@ -0,0 +1,78 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { mutate } from "./mutate.ts"; + +Deno.test("basic", () => { + const obj1 = { + firstName: "Eser", + lastName: "Ozvataf", + aliases: [], + }; + + const result = mutate(obj1, (x) => x.firstName = "Helo"); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals( + result, + { + firstName: "Helo", + lastName: "Ozvataf", + aliases: [], + }, + ); +}); + +Deno.test("array-push", () => { + const obj1 = { + firstName: "Eser", + lastName: "Ozvataf", + aliases: > [], + }; + + const result = mutate(obj1, (x) => { + x.firstName = "Helo"; + + x.aliases.push("laroux"); + }); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals( + result, + { + firstName: "Helo", + lastName: "Ozvataf", + aliases: [ + "laroux", + ], + }, + ); +}); + +Deno.test("with-class", () => { + class Dummy { + items: Array; + + constructor() { + this.items = []; + } + } + + const obj1 = new Dummy(); + + const result = mutate(obj1, (x) => { + x.items.push("laroux"); + }); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(result.constructor, Dummy); + assert.assertEquals(Object.keys(result), ["items"]); + assert.assertEquals( + result.items, + [ + "laroux", + ], + ); +}); diff --git a/fp/mutate.ts b/pkg/fp/mutate.ts similarity index 100% rename from fp/mutate.ts rename to pkg/fp/mutate.ts diff --git a/pkg/fp/pick-from-array.test.ts b/pkg/fp/pick-from-array.test.ts new file mode 100644 index 00000000..8e83ea09 --- /dev/null +++ b/pkg/fp/pick-from-array.test.ts @@ -0,0 +1,72 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { pickFromArray } from "./pick-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const arr2 = [2, 3, 6]; + + const result = pickFromArray(arr1, arr2); + + assert.assertNotStrictEquals(result.items, arr1); + assert.assertNotStrictEquals(result.items, arr2); + assert.assertEquals(result.items.length, 2); + assert.assertEquals(result.items, [2, 3]); + + assert.assertNotStrictEquals(result.rest, arr1); + assert.assertNotStrictEquals(result.rest, arr2); + assert.assertEquals(result.rest.length, 3); + assert.assertEquals(result.rest, [1, 4, 5]); +}); + +Deno.test("with-generator-1", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + + const arr1 = [2, 3, 6]; + + const generated1 = gen1(); + const result = pickFromArray(generated1, arr1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.items, generated1); + assert.assertNotStrictEquals(result.items, arr1); + assert.assertEquals(result.items.length, 2); + assert.assertEquals(result.items, [2, 3]); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.rest, generated1); + assert.assertNotStrictEquals(result.rest, arr1); + assert.assertEquals(result.rest.length, 3); + assert.assertEquals(result.rest, [1, 4, 5]); +}); + +Deno.test("with-generator-2", () => { + const arr1 = [1, 2, 3, 4, 5]; + const gen1 = function* () { + yield 2; + yield 3; + yield 6; + }; + + const generated1 = gen1(); + const result = pickFromArray(arr1, generated1); + + assert.assertNotStrictEquals(result.items, arr1); + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.items, generated1); + assert.assertEquals(result.items.length, 2); + assert.assertEquals(result.items, [2, 3]); + + assert.assertNotStrictEquals(result.rest, arr1); + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.rest, generated1); + assert.assertEquals(result.rest.length, 3); + assert.assertEquals(result.rest, [1, 4, 5]); +}); diff --git a/fp/pick-from-array.ts b/pkg/fp/pick-from-array.ts similarity index 100% rename from fp/pick-from-array.ts rename to pkg/fp/pick-from-array.ts diff --git a/pkg/fp/pick-from-object.test.ts b/pkg/fp/pick-from-object.test.ts new file mode 100644 index 00000000..5a68f2cf --- /dev/null +++ b/pkg/fp/pick-from-object.test.ts @@ -0,0 +1,19 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { pickFromObject } from "./pick-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const arr1 = ["b", "c", "f"]; + + const result = pickFromObject(obj1, arr1); + + assert.assertNotStrictEquals(result.items, obj1); + assert.assertEquals(Object.keys(result.items).length, 2); + assert.assertEquals(result.items, { b: 2, c: 3 }); + + assert.assertNotStrictEquals(result.rest, obj1); + assert.assertEquals(Object.keys(result.rest).length, 3); + assert.assertEquals(result.rest, { a: 1, d: 4, e: 5 }); +}); diff --git a/fp/pick-from-object.ts b/pkg/fp/pick-from-object.ts similarity index 100% rename from fp/pick-from-object.ts rename to pkg/fp/pick-from-object.ts diff --git a/pkg/fp/pipe.test.ts b/pkg/fp/pipe.test.ts new file mode 100644 index 00000000..47e0d024 --- /dev/null +++ b/pkg/fp/pipe.test.ts @@ -0,0 +1,17 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { pipe } from "./pipe.ts"; + +Deno.test("basic", () => { + const lower = (x: string) => x.toLowerCase(); + const chars = (x: string) => x.replace(/[^\w \\-]+/g, ""); + const spaces = (x: string) => x.split(" "); + const dashes = (x: Array) => x.join("-"); + + const slug = pipe(lower, chars, spaces, dashes); + + const result = slug("Hello World!"); + + assert.assertEquals(result, "hello-world"); +}); diff --git a/pkg/fp/pipe.ts b/pkg/fp/pipe.ts new file mode 100644 index 00000000..80e68e6b --- /dev/null +++ b/pkg/fp/pipe.ts @@ -0,0 +1,19 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as functions from "@eser/standards/functions"; +import * as utilities from "@eser/standards/utilities"; + +export const pipe = < + // deno-lint-ignore no-explicit-any + TF extends functions.ArgList>, +>( + ...funcs: TF +): utilities.First => { + return funcs.reduce( + // deno-lint-ignore no-explicit-any + (previousFunction, currentFunction) => (...args: functions.ArgList) => + currentFunction(previousFunction(...args)), + ); +}; + +export { pipe as default }; diff --git a/pkg/fp/prepend-to-array.test.ts b/pkg/fp/prepend-to-array.test.ts new file mode 100644 index 00000000..379fd2c9 --- /dev/null +++ b/pkg/fp/prepend-to-array.test.ts @@ -0,0 +1,31 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { prependToArray } from "./prepend-to-array.ts"; + +Deno.test("basic", () => { + const arr1 = ["b", "c"]; + const str1 = "a"; + + const result = prependToArray(arr1, str1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, ["a", "b", "c"]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield "b"; + yield "c"; + }; + const str1 = "a"; + + const generated1 = gen1(); + const result = prependToArray(generated1, str1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, ["a", "b", "c"]); +}); diff --git a/fp/prepend-to-array.ts b/pkg/fp/prepend-to-array.ts similarity index 100% rename from fp/prepend-to-array.ts rename to pkg/fp/prepend-to-array.ts diff --git a/pkg/fp/prepend-to-object.test.ts b/pkg/fp/prepend-to-object.test.ts new file mode 100644 index 00000000..445ec0cd --- /dev/null +++ b/pkg/fp/prepend-to-object.test.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { prependToObject } from "./prepend-to-object.ts"; + +Deno.test("basic", () => { + const obj1 = { b: 2, c: 3 }; + const obj2 = { a: 1 }; + + const result = prependToObject(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertNotStrictEquals(result, obj2); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: 1, b: 2, c: 3 }); +}); diff --git a/fp/prepend-to-object.ts b/pkg/fp/prepend-to-object.ts similarity index 100% rename from fp/prepend-to-object.ts rename to pkg/fp/prepend-to-object.ts diff --git a/pkg/fp/remove-first-match-from-array.test.ts b/pkg/fp/remove-first-match-from-array.test.ts new file mode 100644 index 00000000..9be9ea48 --- /dev/null +++ b/pkg/fp/remove-first-match-from-array.test.ts @@ -0,0 +1,35 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeFirstMatchFromArray } from "./remove-first-match-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 5, 2, 3, 4, 5]; + const func1 = (x: number) => x === 5; + + const result = removeFirstMatchFromArray(arr1, func1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [1, 2, 3, 4, 5]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 5; + yield 2; + yield 3; + yield 4; + yield 5; + }; + const func1 = (x: number) => x === 5; + + const generated1 = gen1(); + const result = removeFirstMatchFromArray(generated1, func1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [1, 2, 3, 4, 5]); +}); diff --git a/fp/remove-first-match-from-array.ts b/pkg/fp/remove-first-match-from-array.ts similarity index 100% rename from fp/remove-first-match-from-array.ts rename to pkg/fp/remove-first-match-from-array.ts diff --git a/pkg/fp/remove-first-match-from-object.test.ts b/pkg/fp/remove-first-match-from-object.test.ts new file mode 100644 index 00000000..742c2a98 --- /dev/null +++ b/pkg/fp/remove-first-match-from-object.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeFirstMatchFromObject } from "./remove-first-match-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, f: 5, b: 2, c: 3, d: 4, e: 5 }; + const func1 = (x: number) => x === 5; + + const result = removeFirstMatchFromObject(obj1, func1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 5); + assert.assertEquals(result, { a: 1, b: 2, c: 3, d: 4, e: 5 }); +}); diff --git a/fp/remove-first-match-from-object.ts b/pkg/fp/remove-first-match-from-object.ts similarity index 100% rename from fp/remove-first-match-from-object.ts rename to pkg/fp/remove-first-match-from-object.ts diff --git a/pkg/fp/remove-index-from-array.test.ts b/pkg/fp/remove-index-from-array.test.ts new file mode 100644 index 00000000..7f29e936 --- /dev/null +++ b/pkg/fp/remove-index-from-array.test.ts @@ -0,0 +1,36 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeIndexFromArray } from "./remove-index-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const int1 = 2; + const int2 = 3; + + const result = removeIndexFromArray(arr1, int1, int2); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 2, 5]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + const int1 = 2; + const int2 = 3; + + const generated1 = gen1(); + const result = removeIndexFromArray(generated1, int1, int2); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 2, 5]); +}); diff --git a/fp/remove-index-from-array.ts b/pkg/fp/remove-index-from-array.ts similarity index 100% rename from fp/remove-index-from-array.ts rename to pkg/fp/remove-index-from-array.ts diff --git a/pkg/fp/remove-key-from-object.test.ts b/pkg/fp/remove-key-from-object.test.ts new file mode 100644 index 00000000..bd691730 --- /dev/null +++ b/pkg/fp/remove-key-from-object.test.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeKeyFromObject } from "./remove-key-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const str1 = "b"; + const str2 = "c"; + + const result = removeKeyFromObject(obj1, str1, str2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: 1, d: 4, e: 5 }); +}); diff --git a/fp/remove-key-from-object.ts b/pkg/fp/remove-key-from-object.ts similarity index 100% rename from fp/remove-key-from-object.ts rename to pkg/fp/remove-key-from-object.ts diff --git a/pkg/fp/remove-value-from-array.test.ts b/pkg/fp/remove-value-from-array.test.ts new file mode 100644 index 00000000..16bda2eb --- /dev/null +++ b/pkg/fp/remove-value-from-array.test.ts @@ -0,0 +1,36 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeValueFromArray } from "./remove-value-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const int1 = 2; + const int2 = 3; + + const result = removeValueFromArray(arr1, int1, int2); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 4, 5]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + const int1 = 2; + const int2 = 3; + + const generated1 = gen1(); + const result = removeValueFromArray(generated1, int1, int2); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 3); + assert.assertEquals(result, [1, 4, 5]); +}); diff --git a/fp/remove-value-from-array.ts b/pkg/fp/remove-value-from-array.ts similarity index 100% rename from fp/remove-value-from-array.ts rename to pkg/fp/remove-value-from-array.ts diff --git a/pkg/fp/remove-value-from-object.test.ts b/pkg/fp/remove-value-from-object.test.ts new file mode 100644 index 00000000..881a424f --- /dev/null +++ b/pkg/fp/remove-value-from-object.test.ts @@ -0,0 +1,16 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { removeValueFromObject } from "./remove-value-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: "Ia", b: "IIb", c: "IIIc", d: "IVd", e: "Ve" }; + const str1 = "IIb"; + const str2 = "IIIc"; + + const result = removeValueFromObject(obj1, str1, str2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { a: "Ia", d: "IVd", e: "Ve" }); +}); diff --git a/fp/remove-value-from-object.ts b/pkg/fp/remove-value-from-object.ts similarity index 100% rename from fp/remove-value-from-object.ts rename to pkg/fp/remove-value-from-object.ts diff --git a/pkg/fp/reverse-array.test.ts b/pkg/fp/reverse-array.test.ts new file mode 100644 index 00000000..6b0ceaff --- /dev/null +++ b/pkg/fp/reverse-array.test.ts @@ -0,0 +1,32 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { reverseArray } from "./reverse-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + + const result = reverseArray(arr1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [5, 4, 3, 2, 1]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + + const generated1 = gen1(); + const result = reverseArray(generated1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 5); + assert.assertEquals(result, [5, 4, 3, 2, 1]); +}); diff --git a/fp/reverse-array.ts b/pkg/fp/reverse-array.ts similarity index 100% rename from fp/reverse-array.ts rename to pkg/fp/reverse-array.ts diff --git a/pkg/fp/reverse-object.test.ts b/pkg/fp/reverse-object.test.ts new file mode 100644 index 00000000..0697ff4e --- /dev/null +++ b/pkg/fp/reverse-object.test.ts @@ -0,0 +1,14 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { reverseObject } from "./reverse-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + + const result = reverseObject(obj1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 5); + assert.assertEquals(result, { e: 5, d: 4, c: 3, b: 2, a: 1 }); +}); diff --git a/fp/reverse-object.ts b/pkg/fp/reverse-object.ts similarity index 100% rename from fp/reverse-object.ts rename to pkg/fp/reverse-object.ts diff --git a/pkg/fp/split-array.test.ts b/pkg/fp/split-array.test.ts new file mode 100644 index 00000000..0ee2e691 --- /dev/null +++ b/pkg/fp/split-array.test.ts @@ -0,0 +1,44 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { splitArray } from "./split-array.ts"; + +Deno.test("basic", () => { + const arr1 = [1, 2, 3, 4, 5]; + const int1 = 3; + + const result = splitArray(arr1, int1); + + assert.assertNotStrictEquals(result.items, arr1); + assert.assertEquals(result.items.length, 3); + assert.assertEquals(result.items, [1, 2, 3]); + + assert.assertNotStrictEquals(result.rest, arr1); + assert.assertEquals(result.rest.length, 2); + assert.assertEquals(result.rest, [4, 5]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; + }; + + const int1 = 3; + + const generated1 = gen1(); + const result = splitArray(generated1, int1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.items, generated1); + assert.assertEquals(result.items.length, 3); + assert.assertEquals(result.items, [1, 2, 3]); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result.rest, generated1); + assert.assertEquals(result.rest.length, 2); + assert.assertEquals(result.rest, [4, 5]); +}); diff --git a/fp/split-array.ts b/pkg/fp/split-array.ts similarity index 100% rename from fp/split-array.ts rename to pkg/fp/split-array.ts diff --git a/pkg/fp/split-object.test.ts b/pkg/fp/split-object.test.ts new file mode 100644 index 00000000..91a4ceba --- /dev/null +++ b/pkg/fp/split-object.test.ts @@ -0,0 +1,19 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { splitObject } from "./split-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const int1 = 3; + + const result = splitObject(obj1, int1); + + assert.assertNotStrictEquals(result.items, obj1); + assert.assertEquals(Object.keys(result.items).length, 3); + assert.assertEquals(result.items, { a: 1, b: 2, c: 3 }); + + assert.assertNotStrictEquals(result.rest, obj1); + assert.assertEquals(Object.keys(result.rest).length, 2); + assert.assertEquals(result.rest, { d: 4, e: 5 }); +}); diff --git a/fp/split-object.ts b/pkg/fp/split-object.ts similarity index 100% rename from fp/split-object.ts rename to pkg/fp/split-object.ts diff --git a/pkg/fp/take-from-array.test.ts b/pkg/fp/take-from-array.test.ts new file mode 100644 index 00000000..e7016577 --- /dev/null +++ b/pkg/fp/take-from-array.test.ts @@ -0,0 +1,32 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { takeFromArray } from "./take-from-array.ts"; + +Deno.test("basic", () => { + const arr1 = ["a", "b", "c"]; + const int1 = 2; + + const result = takeFromArray(arr1, int1); + + assert.assertNotStrictEquals(result, arr1); + assert.assertEquals(result.length, 2); + assert.assertEquals(result, ["a", "b"]); +}); + +Deno.test("with-generator", () => { + const gen1 = function* () { + yield "a"; + yield "b"; + yield "c"; + }; + const int1 = 2; + + const generated1 = gen1(); + const result = takeFromArray(generated1, int1); + + // deno-lint-ignore no-explicit-any + assert.assertNotStrictEquals( result, generated1); + assert.assertEquals(result.length, 2); + assert.assertEquals(result, ["a", "b"]); +}); diff --git a/fp/take-from-array.ts b/pkg/fp/take-from-array.ts similarity index 100% rename from fp/take-from-array.ts rename to pkg/fp/take-from-array.ts diff --git a/pkg/fp/take-from-object.test.ts b/pkg/fp/take-from-object.test.ts new file mode 100644 index 00000000..720d4a4e --- /dev/null +++ b/pkg/fp/take-from-object.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { takeFromObject } from "./take-from-object.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3 }; + const int1 = 2; + + const result = takeFromObject(obj1, int1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 2); + assert.assertEquals(result, { a: 1, b: 2 }); +}); diff --git a/fp/take-from-object.ts b/pkg/fp/take-from-object.ts similarity index 100% rename from fp/take-from-object.ts rename to pkg/fp/take-from-object.ts diff --git a/fp/wth.bench.ts b/pkg/fp/wth.bench.ts similarity index 61% rename from fp/wth.bench.ts rename to pkg/fp/wth.bench.ts index 043da669..bdc0eb2c 100644 --- a/fp/wth.bench.ts +++ b/pkg/fp/wth.bench.ts @@ -1,15 +1,15 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { wth } from "./wth.ts"; const group = "wth"; -runtime.current.bench("cool/fp/wth", { group, baseline: true }, () => { +jsRuntime.current.bench("eser/fp/wth", { group, baseline: true }, () => { wth({ a: 1 }, { b: 2 }); }); -runtime.current.bench("spread operator", { group }, () => { +jsRuntime.current.bench("spread operator", { group }, () => { const instance = { a: 1 }; const mapping = { b: 2 }; diff --git a/pkg/fp/wth.test.ts b/pkg/fp/wth.test.ts new file mode 100644 index 00000000..0227b2f2 --- /dev/null +++ b/pkg/fp/wth.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { wth } from "./wth.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const obj2 = { b: 6, f: 8 }; + + const result = wth(obj1, obj2); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 6); + assert.assertEquals(result, { a: 1, b: 6, c: 3, d: 4, e: 5, f: 8 }); +}); diff --git a/fp/wth.ts b/pkg/fp/wth.ts similarity index 100% rename from fp/wth.ts rename to pkg/fp/wth.ts diff --git a/fp/wthout.bench.ts b/pkg/fp/wthout.bench.ts similarity index 71% rename from fp/wthout.bench.ts rename to pkg/fp/wthout.bench.ts index 665f0502..4ee4b543 100644 --- a/fp/wthout.bench.ts +++ b/pkg/fp/wthout.bench.ts @@ -1,12 +1,12 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runtime from "../standards/runtime.ts"; +import * as jsRuntime from "@eser/standards/js-runtime"; import { wthout } from "./wthout.ts"; import lodashReject from "npm:lodash.reject"; const group = "wthout"; -runtime.current.bench("cool/fp/wthout", { group, baseline: true }, () => { +jsRuntime.current.bench("eser/fp/wthout", { group, baseline: true }, () => { const student = { id: 1, name: "John Doe", @@ -17,7 +17,7 @@ runtime.current.bench("cool/fp/wthout", { group, baseline: true }, () => { wthout(student, "name", "age"); }); -runtime.current.bench("npm:lodash.reject", { group }, () => { +jsRuntime.current.bench("npm:lodash.reject", { group }, () => { const student = { id: 1, name: "John Doe", diff --git a/pkg/fp/wthout.test.ts b/pkg/fp/wthout.test.ts new file mode 100644 index 00000000..9d71e0f7 --- /dev/null +++ b/pkg/fp/wthout.test.ts @@ -0,0 +1,15 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { wthout } from "./wthout.ts"; + +Deno.test("basic", () => { + const obj1 = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const arr1 = ["a", "d"]; + + const result = wthout(obj1, ...arr1); + + assert.assertNotStrictEquals(result, obj1); + assert.assertEquals(Object.keys(result).length, 3); + assert.assertEquals(result, { b: 2, c: 3, e: 5 }); +}); diff --git a/fp/wthout.ts b/pkg/fp/wthout.ts similarity index 100% rename from fp/wthout.ts rename to pkg/fp/wthout.ts diff --git a/pkg/functions/deno.jsonc b/pkg/functions/deno.jsonc new file mode 100644 index 00000000..36bcefcd --- /dev/null +++ b/pkg/functions/deno.jsonc @@ -0,0 +1,11 @@ +{ + "name": "@eser/functions", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/testing": "jsr:@std/testing@^0.225.3" + }, + "exports": { + ".": "./mod.ts" + } +} diff --git a/pkg/functions/fn.test.ts b/pkg/functions/fn.test.ts new file mode 100644 index 00000000..f887c225 --- /dev/null +++ b/pkg/functions/fn.test.ts @@ -0,0 +1,123 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import * as mock from "@std/testing/mock"; +import { Ok, type Result } from "./results.ts"; +import { fn } from "./fn.ts"; + +Deno.test("simple fn().run()", async () => { + const spyFn = mock.spy(); + + const fns = fn>( + () => { + spyFn(); + + return Ok("Testing"); + }, + ); + + const result = await fns.run(); + + mock.assertSpyCalls(spyFn, 1); + assert.assertEquals(result[0]?.payload, "Testing"); +}); + +Deno.test("simple fn().iterate()", async () => { + const spyFn = mock.spy(); + + const fns = fn( + function* () { + spyFn(); + + yield Ok("hello"); + yield Ok("world"); + }, + ); + + const items = []; + for await (const item of fns.iterate()) { + items.push(item.payload); + } + + mock.assertSpyCalls(spyFn, 1); + assert.assertEquals(items, ["hello", "world"]); +}); + +Deno.test("multiple fn().iterate()", async () => { + const spyFn1 = mock.spy(); + const spyFn2 = mock.spy(); + + const fns = fn>( + async function* (c) { + spyFn1(); + + yield Ok("hello"); + yield* c.next(); + }, + async function* () { + spyFn2(); + + yield Ok("world"); + }, + ); + + const items = []; + for await (const item of fns.iterate()) { + items.push(item.payload); + } + + mock.assertSpyCalls(spyFn1, 1); + mock.assertSpyCalls(spyFn2, 1); + assert.assertEquals(items, ["hello", "world"]); +}); + +Deno.test("fn().use().run()", async () => { + const spyFn1 = mock.spy(); + const spyFn2 = mock.spy(); + + const fns = fn>() + .use( + async function* (c) { + spyFn1(); + + yield Ok("hello"); + yield* c.next(); + }, + ) + .use( + async function* () { + spyFn2(); + + yield Ok("world"); + }, + ); + + const items = await fns.run(); + + mock.assertSpyCalls(spyFn1, 1); + mock.assertSpyCalls(spyFn2, 1); + assert.assertEquals(items, [ + { payload: "hello" }, + { payload: "world" }, + ]); +}); + +Deno.test("alias fn()", async () => { + const spyFn = mock.spy(); + + const express = fn; + + const result = await express>() + .use( + async function* (c) { + spyFn(); + + yield Ok("Testing"); + yield* c.next(); + }, + ) + .run(); + + mock.assertSpyCalls(spyFn, 1); + assert.assertEquals(result[0]?.payload, "Testing"); +}); diff --git a/functions/fn.ts b/pkg/functions/fn.ts similarity index 75% rename from functions/fn.ts rename to pkg/functions/fn.ts index a2f3348a..2f411ff9 100644 --- a/functions/fn.ts +++ b/pkg/functions/fn.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import { type ArgList } from "../standards/functions.ts"; +import * as functions from "@eser/standards/functions"; // deno-lint-ignore no-explicit-any export type AcceptableResult = any; @@ -28,7 +28,8 @@ export type Fn< TR = AcceptableResult, TS extends State = State, > = { - (context: Context, ...args: ArgList): FnResult; + // deno-lint-ignore no-explicit-any + (context: Context, ...args: functions.ArgList): FnResult; }; export type Pipeline< @@ -37,22 +38,24 @@ export type Pipeline< > = { use: (...fns: ReadonlyArray>) => Pipeline; set: (fn: Fn) => Pipeline; - iterate: (...args: ArgList) => AsyncGenerator; - run: (...args: ArgList) => Promise>; + // deno-lint-ignore no-explicit-any + iterate: (...args: functions.ArgList) => AsyncGenerator; + // deno-lint-ignore no-explicit-any + run: (...args: functions.ArgList) => Promise>; }; -export const fn = function < +export const fn = < TR = AcceptableResult, TS extends State = State, ->(...fns: ReadonlyArray>): Pipeline { +>(...fns: ReadonlyArray>): Pipeline => { let target = fns.at(-1); - const stack = fns.slice(0, -1); + let stack = fns.slice(0, -1); const use = function ( this: Pipeline, ...fns: ReadonlyArray> ) { - stack.push(...fns); + stack = [...stack, ...fns]; return this; }; @@ -65,7 +68,8 @@ export const fn = function < const iterate = async function* ( this: Pipeline, - ...args: ArgList + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList ) { let prevIndex = -1; @@ -108,7 +112,11 @@ export const fn = function < yield* jumper(0); }; - const run = async function (this: Pipeline, ...args: ArgList) { + const run = async function ( + this: Pipeline, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ) { const results: Array = []; for await (const item of this.iterate(...args)) { diff --git a/functions/mod.ts b/pkg/functions/mod.ts similarity index 100% rename from functions/mod.ts rename to pkg/functions/mod.ts diff --git a/functions/results.ts b/pkg/functions/results.ts similarity index 100% rename from functions/results.ts rename to pkg/functions/results.ts diff --git a/pkg/jsx-runtime.test/deno.jsonc b/pkg/jsx-runtime.test/deno.jsonc new file mode 100644 index 00000000..c99c39fe --- /dev/null +++ b/pkg/jsx-runtime.test/deno.jsonc @@ -0,0 +1,7 @@ +{ + "imports": { + "@eser/jsx-runtime": "../jsx-runtime/mod.ts", + "@std/assert": "jsr:@std/assert@^1.0.0", + "@std/testing": "jsr:@std/testing@^0.225.3" + } +} diff --git a/pkg/jsx-runtime.test/root.test.tsx b/pkg/jsx-runtime.test/root.test.tsx new file mode 100644 index 00000000..03c028c6 --- /dev/null +++ b/pkg/jsx-runtime.test/root.test.tsx @@ -0,0 +1,41 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +// import * as mock from "@std/testing/mock"; +// import * as jsxRuntimeInternals from "@eser/jsx-runtime/internals"; +import * as root from "./root.tsx"; + +Deno.test("should return the root component", () => { + const actual = root.Root(); + + assert.assertObjectMatch(actual, { + // constructor: undefined, + type: root.Component, + props: { + "foo": "bar", + "lime-hack": true, + }, + key: null, + ref: null, + }); +}); + +// Deno.test("should call the hook", () => { +// const spyFn = mock.spy(); + +// // deno-lint-ignore no-explicit-any +// const hook = (vnode: any) => { +// if (vnode.props["lime-hack"]) { +// spyFn(); +// } +// }; + +// // @ts-ignore typescript don't recognize this +// jsxRuntimeInternals.options.tagHelperHook = hook; + +// root.Root(); + +// mock.assertSpyCalls(spyFn, 1); + +// jsxRuntimeInternals.options.tagHelperHook = null; +// }); diff --git a/jsx-runtime.test/root.tsx b/pkg/jsx-runtime.test/root.tsx similarity index 100% rename from jsx-runtime.test/root.tsx rename to pkg/jsx-runtime.test/root.tsx diff --git a/pkg/jsx-runtime/deno.jsonc b/pkg/jsx-runtime/deno.jsonc new file mode 100644 index 00000000..a75f7fc4 --- /dev/null +++ b/pkg/jsx-runtime/deno.jsonc @@ -0,0 +1,10 @@ +{ + "name": "@eser/jsx-runtime", + "version": "0.7.20", + "imports": { + "react/jsx-runtime": "https://esm.sh/stable/react@19.0.0-rc-df5f2736-20240712/es2022/jsx-runtime.js" + }, + "exports": { + ".": "./mod.ts" + } +} diff --git a/jsx-runtime/encoder.js b/pkg/jsx-runtime/encoder.ts similarity index 94% rename from jsx-runtime/encoder.js rename to pkg/jsx-runtime/encoder.ts index abbf796d..099ac62b 100644 --- a/jsx-runtime/encoder.js +++ b/pkg/jsx-runtime/encoder.ts @@ -8,8 +8,7 @@ const ENCODED_ENTITIES = /["&<]/; -/** @param {string} str */ -export const encodeEntities = (str) => { +export const encodeEntities = (str: string) => { // Skip all work for strings with no entities needing encoding: if (str.length === 0 || ENCODED_ENTITIES.test(str) === false) { return str; diff --git a/pkg/jsx-runtime/mod.ts b/pkg/jsx-runtime/mod.ts new file mode 100644 index 00000000..0b8599c1 --- /dev/null +++ b/pkg/jsx-runtime/mod.ts @@ -0,0 +1,54 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as reactJsxRuntime from "react/jsx-runtime"; +import { encodeEntities } from "./encoder.ts"; + +export const jsx = reactJsxRuntime.jsx; + +// deno-lint-ignore no-explicit-any +type VNode = object | any[]; // reactJsxRuntime.JSX.Element; + +/** + * Escape a dynamic child passed to `jsxTemplate`. This function + * is not expected to be used directly, but rather through a + * precompile JSX transform + */ +export function jsxEscape(value: unknown): string | null | VNode { + if ( + value == null || + typeof value === "boolean" || + typeof value === "function" + ) { + return null; + } + + if (typeof value === "object") { + // Check for VNode + if (value.constructor === undefined) { + return value; + } + + if (Array.isArray(value)) { + for (let i = 0; i < value.length; i++) { + value[i] = jsxEscape(value[i]); + } + + return value; + } + } + + return encodeEntities("" + value); +} + +/** + * Create a template vnode. This function is not expected to be + * used directly, but rather through a precompile JSX transform + */ +export const jsxTemplate = ( + templates: string[], + ...exprs: ReadonlyArray +): VNode => { + const vnode = jsx(reactJsxRuntime.Fragment, { tpl: templates, exprs }); + + return vnode; +}; diff --git a/lime.test/build.ts b/pkg/lime.test/build.ts similarity index 82% rename from lime.test/build.ts rename to pkg/lime.test/build.ts index 1317ada7..115ff44b 100644 --- a/lime.test/build.ts +++ b/pkg/lime.test/build.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as collector from "../collector/mod.ts"; +import * as collector from "@eser/collector"; await collector.buildManifest( Deno.stdout.writable, diff --git a/lime.test/main.ts b/pkg/lime.test/main.ts similarity index 90% rename from lime.test/main.ts rename to pkg/lime.test/main.ts index 7253b917..41fc9762 100644 --- a/lime.test/main.ts +++ b/pkg/lime.test/main.ts @@ -1,18 +1,18 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as lime from "../lime/mod.ts"; +import * as lime from "@eser/lime"; // TODO(@eser): lime traverses all files in the directory and looks for // exported function named "metadata". when it finds it, it calls it and // registers the result into a manifest. -// export function metadata(registry) { +// export const metadata = (registry) => { // registry.addReactRoot("/hello", ); // registry.addHook("/hook", fn); // registry.addDb("postgres", "postgres://localhost:5432"); // registry.addStaticAsset("/static", "./static"); // ... -// } +// }; export const instance = lime.builder() .setBaseUrl(import.meta.url) diff --git a/lime.test/manifest.jsonc b/pkg/lime.test/manifest.jsonc similarity index 100% rename from lime.test/manifest.jsonc rename to pkg/lime.test/manifest.jsonc diff --git a/lime.test/manifest.yaml b/pkg/lime.test/manifest.yaml similarity index 100% rename from lime.test/manifest.yaml rename to pkg/lime.test/manifest.yaml diff --git a/lime.test/pkg/app/mod.tsx b/pkg/lime.test/pkg/app/mod.tsx similarity index 100% rename from lime.test/pkg/app/mod.tsx rename to pkg/lime.test/pkg/app/mod.tsx diff --git a/lime/deno-config-deprecated.ts b/pkg/lime/deno-config-deprecated.ts similarity index 91% rename from lime/deno-config-deprecated.ts rename to pkg/lime/deno-config-deprecated.ts index 08439caa..b6bf44b1 100644 --- a/lime/deno-config-deprecated.ts +++ b/pkg/lime/deno-config-deprecated.ts @@ -1,6 +1,6 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as fileLoader from "../file-loader/mod.ts"; +import * as fileLoader from "@eser/config/file"; export type DenoConfig = { imports?: Record; @@ -26,6 +26,7 @@ export const loadDenoConfig = async ( const denoConfig = await fileLoader.load( baseDir, ["deno.json", "deno.jsonc"], + undefined, false, ); diff --git a/pkg/lime/deno.jsonc b/pkg/lime/deno.jsonc new file mode 100644 index 00000000..c2d0e377 --- /dev/null +++ b/pkg/lime/deno.jsonc @@ -0,0 +1,8 @@ +{ + "name": "@eser/lime", + "version": "0.7.20", + "imports": {}, + "exports": { + ".": "./mod.ts" + } +} diff --git a/lime/mod.ts b/pkg/lime/mod.ts similarity index 81% rename from lime/mod.ts rename to pkg/lime/mod.ts index e4c9483b..873f277a 100644 --- a/lime/mod.ts +++ b/pkg/lime/mod.ts @@ -8,9 +8,9 @@ export * from "./primitives.ts"; * * @returns {Lime} */ -export const builder = () => { +export const builder = (): primitives.Lime => { const instance = new primitives.Lime(); - instance.setAsDefaultAppServer(); + // instance.setAsDefault(); return instance; }; diff --git a/lime/primitives.ts b/pkg/lime/primitives.ts similarity index 56% rename from lime/primitives.ts rename to pkg/lime/primitives.ts index ef1df860..1df715f6 100644 --- a/lime/primitives.ts +++ b/pkg/lime/primitives.ts @@ -1,8 +1,8 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as runModes from "../standards/run-modes.ts"; -import * as fileLoader from "../file-loader/mod.ts"; -import * as appserver from "../appserver/mod.ts"; +import * as runModes from "@eser/standards/run-modes"; +import * as fileLoader from "@eser/config/file"; +import * as appRuntime from "@eser/app-runtime"; export type LimeExportedSymbol = unknown; @@ -14,41 +14,42 @@ export type LimeOptions = { basePath: string; }; -export type LimeState = { +export type LimeState = appRuntime.AppRuntimeState & { baseUrl: string | null; manifests: Array; options: LimeOptions; }; -export class Lime extends appserver.AppServer { - state: LimeState; - - constructor(options?: Partial) { - super(); +export const createLimeState = (options?: Partial): LimeState => { + return { + ...appRuntime.createAppRuntimeState(), + baseUrl: null, + manifests: [], + options: Object.assign( + { basePath: "" }, + options, + ), + }; +}; - this.state = { - baseUrl: null, - manifests: [], - options: Object.assign( - { basePath: "" }, - options, - ), - }; +export class Lime extends appRuntime.AppRuntime { + constructor(state?: LimeState) { + super(state ?? createLimeState()); } - setBaseUrl(baseUrl: string | null): Lime { + setBaseUrl(baseUrl: string | null): this { this.state.baseUrl = baseUrl; return this; } - addManifest(manifest: LimeManifest): Lime { + addManifest(manifest: LimeManifest): this { this.state.manifests = [...this.state.manifests, manifest]; return this; } - loadManifest(baseDir?: string): Lime { + loadManifest(baseDir?: string): this { const promise = fileLoader.load( fileLoader.resolvePath(baseDir ?? ".", this.state.baseUrl), ["manifest.yaml", "manifest.jsonc", "manifest.json", "manifest.toml"], @@ -61,13 +62,13 @@ export class Lime extends appserver.AppServer { }); // FIXME(@eser) Handle errors. - this.awaits.push(promise); + this.state.awaits = [...this.state.awaits, promise]; return this; } - dev(): Lime { - this.runMode |= runModes.RunModes.Development; + dev(): this { + this.state.runMode |= runModes.RunModes.Development; return this; } diff --git a/pkg/logging/deno.jsonc b/pkg/logging/deno.jsonc new file mode 100644 index 00000000..67e11dc2 --- /dev/null +++ b/pkg/logging/deno.jsonc @@ -0,0 +1,9 @@ +{ + "name": "@eser/logging", + "version": "0.7.20", + "imports": {}, + "exports": { + ".": "./mod.ts", + "./logger": "./logger.ts" + } +} diff --git a/logging/formatters.ts b/pkg/logging/formatters.ts similarity index 79% rename from logging/formatters.ts rename to pkg/logging/formatters.ts index 756c54ed..7eda3ba3 100644 --- a/logging/formatters.ts +++ b/pkg/logging/formatters.ts @@ -6,13 +6,14 @@ // Copyright (c) 2023-2024 Eser Ozvataf and other contributors // Copyright (c) 2021-2023 the Deno authors -import * as logging from "../standards/logging.ts"; -import * as functions from "../standards/functions.ts"; +import * as logging from "@eser/standards/logging"; +import * as functions from "@eser/standards/functions"; import * as logger from "./logger.ts"; export type FormatterFn = (logRecord: logger.LogRecord) => string; -const flattenArgs = (args: functions.ArgList): unknown => { +// deno-lint-ignore no-explicit-any +const flattenArgs = (args: functions.ArgList): unknown => { if (args.length > 1) { return args; } diff --git a/pkg/logging/logger.ts b/pkg/logging/logger.ts new file mode 100644 index 00000000..12478e1c --- /dev/null +++ b/pkg/logging/logger.ts @@ -0,0 +1,256 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +// This file contains code from deno std lib (https://github.com/denoland/deno_std), +// which is a standard library, licensed under the MIT license. + +// Copyright (c) 2023-2024 Eser Ozvataf and other contributors +// Copyright (c) 2021-2023 the Deno authors + +import * as jsRuntime from "@eser/standards/js-runtime"; +import * as logging from "@eser/standards/logging"; +import * as functions from "@eser/standards/functions"; +import * as formatters from "./formatters.ts"; + +export const DEFAULT_LEVEL = logging.Severities.Info; + +export type LogRecord = { + message: string; + // deno-lint-ignore no-explicit-any + args: functions.ArgList; + datetime: Date; + severity: logging.Severity; + loggerName: string; +}; + +export type LoggerState = { + readonly loggerName: string; + readonly targetStream: WritableStream; + loglevel: logging.Severity; + readonly formatter: formatters.FormatterFn; + encoder: TextEncoder; +}; + +export const createLoggerState = ( + loggerName: string, + targetStream: WritableStream, + loglevel: logging.Severity = DEFAULT_LEVEL, + formatter: formatters.FormatterFn = formatters.jsonFormatter, +): LoggerState => { + return { + loggerName: loggerName, + targetStream: targetStream, + loglevel: loglevel, + formatter: formatter, + encoder: new TextEncoder(), + }; +}; + +export class Logger implements logging.Logger { + readonly state: LoggerState; + + constructor(state: LoggerState) { + this.state = state; + } + + /** + * If the level of the logger is greater than the level to log, then nothing + * is logged, otherwise a log record is passed to each log handler. `message` data + * passed in is returned. If a function is passed in, it is only evaluated + * if the message will be logged and the return value will be the result of the + * function, not the function itself, unless the function isn't called, in which + * case undefined is returned. All types are coerced to strings for logging. + */ + async log( + severity: logging.Severity, + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + if (this.state.loglevel > severity) { + return message instanceof Function ? undefined : message; + } + + let fnResult: T | undefined; + let logMessage: string; + if (message instanceof Function) { + fnResult = message(); + logMessage = this.asString(fnResult); + } else { + logMessage = this.asString(message); + } + const record: LogRecord = { + message: logMessage, + args: args, + datetime: new Date(), + severity: severity, + loggerName: this.state.loggerName, + }; + + const outputWriter = this.state.targetStream.getWriter(); + await outputWriter.ready; + + const formatted = this.state.formatter(record); + const encoded = this.state.encoder.encode(formatted); + await outputWriter.write(encoded); + + outputWriter.releaseLock(); + + return message instanceof Function ? fnResult : message; + } + + asString(data: unknown, isProperty = false): string { + if (typeof data === "string") { + if (isProperty) { + return `"${data}"`; + } + + return data; + } + + if ( + data === null || + typeof data === "number" || + typeof data === "bigint" || + typeof data === "boolean" || + typeof data === "undefined" || + typeof data === "symbol" + ) { + return String(data); + } + + if (data instanceof Error) { + return data.stack!; + } + + if (typeof data === "object") { + return `{${ + Object.entries(data) + .map(([k, v]) => `"${k}":${this.asString(v, true)}`) + .join(",") + }}`; + } + + return "undefined"; + } + + debug( + message: () => T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + debug( + // deno-lint-ignore no-explicit-any + message: T extends functions.GenericFunction ? never : T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + debug( + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + return this.log(logging.Severities.Debug, message, ...args); + } + + info( + message: () => T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + info( + // deno-lint-ignore no-explicit-any + message: T extends functions.GenericFunction ? never : T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + info( + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + return this.log(logging.Severities.Info, message, ...args); + } + + warn( + message: () => T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + warn( + // deno-lint-ignore no-explicit-any + message: T extends functions.GenericFunction ? never : T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + warn( + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + return this.log(logging.Severities.Warning, message, ...args); + } + + error( + message: () => T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + error( + // deno-lint-ignore no-explicit-any + message: T extends functions.GenericFunction ? never : T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + error( + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + return this.log(logging.Severities.Error, message, ...args); + } + + critical( + message: () => T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + critical( + // deno-lint-ignore no-explicit-any + message: T extends functions.GenericFunction ? never : T, + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise; + critical( + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList + ): Promise { + return this.log(logging.Severities.Critical, message, ...args); + } +} + +export const current: Logger = new Logger( + createLoggerState( + "default", + jsRuntime.current.getStdout(), + DEFAULT_LEVEL, + ), +); diff --git a/logging/mod.ts b/pkg/logging/mod.ts similarity index 100% rename from logging/mod.ts rename to pkg/logging/mod.ts diff --git a/mod.ts b/pkg/mod.ts similarity index 68% rename from mod.ts rename to pkg/mod.ts index 83dfc36b..c1336d5e 100644 --- a/mod.ts +++ b/pkg/mod.ts @@ -1,13 +1,16 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -export * as appserver from "./appserver/mod.ts"; +export * as appRuntime from "./app-runtime/mod.ts"; +export * as bundler from "./bundler/mod.ts"; +export * as collector from "./collector/mod.ts"; +export * as config from "./config/mod.ts"; export * as di from "./di/mod.ts"; // export * as directives from "./directives/mod.ts"; -export * as dotenv from "./dotenv/mod.ts"; export * as events from "./events/mod.ts"; export * as fp from "./fp/mod.ts"; export * as functions from "./functions/mod.ts"; // export * as jsxRuntime from "./jsx-runtime/mod.ts"; // export * as lime from "./lime/mod.ts"; +export * as logging from "./logging/mod.ts"; export * as parsing from "./parsing/mod.ts"; export * as standards from "./standards/mod.ts"; diff --git a/parsing/README.md b/pkg/parsing/README.md similarity index 77% rename from parsing/README.md rename to pkg/parsing/README.md index 1fb2c87e..fe1c9463 100644 --- a/parsing/README.md +++ b/pkg/parsing/README.md @@ -1,6 +1,6 @@ -# 〰️ [cool/parsing](./) +# 〰️ [@eser/parsing](./) -`cool/parsing` is a comprehensive parsing library designed to analyze and +`@eser/parsing` is a comprehensive parsing library designed to analyze and tokenize strings. While it includes a lexer component, its capabilities extend beyond simple lexing, providing a flexible and efficient way to parse strings into meaningful tokens or an abstract syntax tree. @@ -17,7 +17,7 @@ programming languages to understanding natural language. A tokenizer is a crucial component in compilers and interpreters. It reads the input of raw text and breaks it down into a series of tokens, which are sequences of characters with a known meaning. The lexer included in -`cool/parsing` is designed to be both efficient and versatile, making it +`@eser/parsing` is designed to be both efficient and versatile, making it suitable for a wide range of input sources. ### Parser @@ -38,7 +38,7 @@ used extensively in compilers and source code analysis tools. ## 🛠 Usage -Here you'll find a list of features provided by `cool/parsing` along with brief +Here you'll find a list of features provided by `eser/parsing` along with brief descriptions and usage examples. ### Tokenizing from source @@ -46,9 +46,9 @@ descriptions and usage examples. **Basic usage:** ```js -import { simpleTokens, Tokenizer } from "$cool/parsing/mod.ts"; +import { createTokenizerState. simpleTokens, Tokenizer } from "@eser/parsing"; -const lexer = new Tokenizer(simpleTokens); +const lexer = new Tokenizer(createTokenizerState(simpleTokens)); for (const token of lexer.tokenizeFromString("1 + 2")) { console.log(token); @@ -58,9 +58,9 @@ for (const token of lexer.tokenizeFromString("1 + 2")) { **Using a different token dictionary:** ```js -import { extendedTokens, Tokenizer } from "$cool/parsing/mod.ts"; +import { createTokenizerState, extendedTokens, Tokenizer } from "@eser/parsing"; -const lexer = new Tokenizer(extendedTokens); +const lexer = new Tokenizer(createTokenizerState(extendedTokens)); const tokens = Array.from( lexer.tokenizeFromString("cout << 'hello C++' << endl;"), @@ -72,11 +72,11 @@ console.log(tokens); **Reading from a ReadableStream:** ```js -import { extendedTokens, Tokenizer } from "$cool/parsing/mod.ts"; +import { createTokenizerState, extendedTokens, Tokenizer } from "@eser/parsing"; -const response = await fetch("https://deno.land/x/cool@0.7.13/di/mod.ts"); +const response = await fetch("https://deno.land/x/eser@0.7.13/di/mod.ts"); -const lexer = new Tokenizer(extendedTokens); +const lexer = new Tokenizer(createTokenizerState(extendedTokens)); for await (const token of lexer.tokenize(response.body.getReader())) { console.log(token); @@ -89,7 +89,7 @@ The following is a list of all available methods and their descriptions. ### Tokenizer -**new Tokenizer(tokens)**\ +**new Tokenizer(createTokenizerState(tokens))**\ Tokenizer is a class that can be used to tokenize strings. It is initialized with a list of tokens. diff --git a/pkg/parsing/deno.jsonc b/pkg/parsing/deno.jsonc new file mode 100644 index 00000000..350396ca --- /dev/null +++ b/pkg/parsing/deno.jsonc @@ -0,0 +1,10 @@ +{ + "name": "@eser/parsing", + "version": "0.7.20", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.0" + }, + "exports": { + ".": "./mod.ts" + } +} diff --git a/parsing/lexer/checks/is-alphanumeric.ts b/pkg/parsing/lexer/checks/is-alphanumeric.ts similarity index 100% rename from parsing/lexer/checks/is-alphanumeric.ts rename to pkg/parsing/lexer/checks/is-alphanumeric.ts diff --git a/parsing/lexer/checks/is-numeric.ts b/pkg/parsing/lexer/checks/is-numeric.ts similarity index 100% rename from parsing/lexer/checks/is-numeric.ts rename to pkg/parsing/lexer/checks/is-numeric.ts diff --git a/parsing/lexer/checks/is-whitespace.ts b/pkg/parsing/lexer/checks/is-whitespace.ts similarity index 100% rename from parsing/lexer/checks/is-whitespace.ts rename to pkg/parsing/lexer/checks/is-whitespace.ts diff --git a/pkg/parsing/lexer/lexer.test.ts b/pkg/parsing/lexer/lexer.test.ts new file mode 100644 index 00000000..b424672d --- /dev/null +++ b/pkg/parsing/lexer/lexer.test.ts @@ -0,0 +1,243 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { simpleTokens } from "./tokens/simple.ts"; +import { createTokenizerState, Tokenizer } from "./lexer.ts"; + +Deno.test("operators and symbols", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = "=+-*_/%<>!&|?:;,.()[]{}^"; + + const result = Array.from(lexer.tokenizeFromString(input)); + + assert.assertEquals( + result, + [ + { kind: "T_EQUALS", value: "=" }, + { kind: "T_PLUS", value: "+" }, + { kind: "T_HYPHEN", value: "-" }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_UNDERSCORE", value: "_" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_PERCENT", value: "%" }, + { kind: "T_IS_SMALLER", value: "<" }, + { kind: "T_IS_GREATER", value: ">" }, + { kind: "T_EXCLAMATION_MARK", value: "!" }, + { kind: "T_AMPERSAND", value: "&" }, + { kind: "T_PIPE", value: "|" }, + { kind: "T_QUESTION_MARK", value: "?" }, + { kind: "T_COLON", value: ":" }, + { kind: "T_SEMICOLON", value: ";" }, + { kind: "T_COMMA", value: "," }, + { kind: "T_DOT", value: "." }, + { kind: "T_PARENTHESIS_OPEN", value: "(" }, + { kind: "T_PARENTHESIS_CLOSE", value: ")" }, + { kind: "T_SQUARE_BRACKET_OPEN", value: "[" }, + { kind: "T_SQUARE_BRACKET_CLOSE", value: "]" }, + { kind: "T_CURLY_BRACKET_OPEN", value: "{" }, + { kind: "T_CURLY_BRACKET_CLOSE", value: "}" }, + { kind: "T_CARET", value: "^" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("numbers", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = `1234 5678`; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_NUMERIC", value: "1234" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_NUMERIC", value: "5678" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("whitespace and comments", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = ` // This is a comment + /* This is a + multiline comment */ `; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "This" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "is" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "a" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "comment" }, + { kind: "T_NEWLINE", value: "\n" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "This" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "is" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "a" }, + { kind: "T_NEWLINE", value: "\n" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "multiline" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "comment" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("multiline comments", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = `/* test */`; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_SLASH", value: "/" }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "test" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("singleline comments", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = `// test`; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_SLASH", value: "/" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "test" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("mixed expression", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = `rocketLauncher++ /* Comment */ && test123`; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, + { kind: "T_PLUS", value: "+" }, + { kind: "T_PLUS", value: "+" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "Comment" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ASTERISK", value: "*" }, + { kind: "T_SLASH", value: "/" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_AMPERSAND", value: "&" }, + { kind: "T_AMPERSAND", value: "&" }, + { kind: "T_WHITESPACE", value: " " }, + { kind: "T_ALPHANUMERIC", value: "test123" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("alphanumeric from readable stream", async () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const readableStream = new ReadableStream({ + start(controller) { + controller.enqueue("rocket"); + controller.enqueue("Launcher"); + controller.close(); + }, + }); + + const result = []; + for await (const token of lexer.tokenize(readableStream)) { + result.push(token); + } + + assert.assertEquals( + result, + [ + { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("alphanumeric from string", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + const input = `rocketLauncher`; + + const result = Array.from(lexer.tokenizeFromString(input)); + assert.assertEquals( + result, + [ + { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, + { kind: "T_END", value: "" }, + ], + ); +}); + +Deno.test("alphanumeric from string, buffer boundary", () => { + const lexer = new Tokenizer(createTokenizerState(simpleTokens)); + + lexer._reset(); + const result1 = Array.from(lexer._tokenizeChunk("rocket")); + + assert.assertEquals( + result1, + [], + ); + + const result2 = Array.from(lexer._tokenizeChunk("Launcher")); + assert.assertEquals( + result2, + [], + ); + + const result3 = Array.from(lexer._tokenizeChunk(null)); + assert.assertEquals( + result3, + [ + { kind: "T_ALPHANUMERIC", value: "rocketLauncher" }, + { kind: "T_END", value: "" }, + ], + ); +}); diff --git a/parsing/lexer/lexer.ts b/pkg/parsing/lexer/lexer.ts similarity index 64% rename from parsing/lexer/lexer.ts rename to pkg/parsing/lexer/lexer.ts index 80668e4f..7c30e9c9 100644 --- a/parsing/lexer/lexer.ts +++ b/pkg/parsing/lexer/lexer.ts @@ -10,13 +10,27 @@ export type Token = { value: string | undefined; }; -export class Tokenizer { +export type TokenizerState = { readonly tokenDefs: TokenDefinitions; - _isDone = true; - _buffer = ""; + isDone: boolean; + buffer: string; +}; + +export const createTokenizerState = ( + tokenDefs: TokenDefinitions, +): TokenizerState => { + return { + tokenDefs, + isDone: true, + buffer: "", + }; +}; + +export class Tokenizer { + readonly state: TokenizerState; - constructor(tokenDefs: TokenDefinitions) { - this.tokenDefs = tokenDefs; + constructor(state: TokenizerState) { + this.state = state; } *tokenizeFromString(input: string): Generator { @@ -37,17 +51,17 @@ export class Tokenizer { *_tokenizeChunk(input: string | null): Generator { if (input === null) { - this._isDone = true; + this.state.isDone = true; } else { - this._buffer += input; + this.state.buffer += input; } let position = 0; - while (position < this._buffer.length) { + while (position < this.state.buffer.length) { let matched = false; - for (const [tokenKind, pattern] of Object.entries(this.tokenDefs)) { + for (const [tokenKind, pattern] of Object.entries(this.state.tokenDefs)) { if (pattern === null) { continue; } @@ -55,7 +69,7 @@ export class Tokenizer { if (pattern.constructor === String) { if ( pattern !== "" && - this._buffer.startsWith(pattern as string, position) + this.state.buffer.startsWith(pattern as string, position) ) { yield { kind: tokenKind, value: pattern } as Token; @@ -69,13 +83,13 @@ export class Tokenizer { } if (pattern.constructor === Function) { - const remainingStr = this._buffer.substring(position); + const remainingStr = this.state.buffer.substring(position); const [value, length, couldContinue] = (pattern as PatternFunction)( remainingStr, ); - if (couldContinue && !this._isDone) { - this._buffer = remainingStr; + if (couldContinue && !this.state.isDone) { + this.state.buffer = remainingStr; return; } @@ -93,19 +107,19 @@ export class Tokenizer { if (!matched) { yield { kind: "T_UNKNOWN", - value: this._buffer[position], + value: this.state.buffer[position], }; position++; } } - if (this._buffer.length > 0) { - this._buffer = this._buffer.substring(position); + if (this.state.buffer.length > 0) { + this.state.buffer = this.state.buffer.substring(position); } - if (this._isDone) { - if (this._buffer.length > 0) { + if (this.state.isDone) { + if (this.state.buffer.length > 0) { return; } @@ -114,7 +128,7 @@ export class Tokenizer { } _reset() { - this._isDone = false; - this._buffer = ""; + this.state.isDone = false; + this.state.buffer = ""; } } diff --git a/parsing/lexer/mod.ts b/pkg/parsing/lexer/mod.ts similarity index 100% rename from parsing/lexer/mod.ts rename to pkg/parsing/lexer/mod.ts diff --git a/parsing/lexer/tokens/definition.ts b/pkg/parsing/lexer/tokens/definition.ts similarity index 100% rename from parsing/lexer/tokens/definition.ts rename to pkg/parsing/lexer/tokens/definition.ts diff --git a/parsing/lexer/tokens/simple.ts b/pkg/parsing/lexer/tokens/simple.ts similarity index 100% rename from parsing/lexer/tokens/simple.ts rename to pkg/parsing/lexer/tokens/simple.ts diff --git a/parsing/mod.ts b/pkg/parsing/mod.ts similarity index 100% rename from parsing/mod.ts rename to pkg/parsing/mod.ts diff --git a/pkg/parsing/parser.test.ts b/pkg/parsing/parser.test.ts new file mode 100644 index 00000000..8aa063a8 --- /dev/null +++ b/pkg/parsing/parser.test.ts @@ -0,0 +1,46 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as assert from "@std/assert"; +import { simpleTokens } from "./lexer/tokens/simple.ts"; +import { createTokenizerState, Tokenizer } from "./lexer/lexer.ts"; +import { sequence, token } from "./parser.ts"; + +Deno.test("tokens", () => { + const parser = token("T_NUMERIC"); + + const tokenizer = new Tokenizer(createTokenizerState(simpleTokens)); + const tokenGenerator = tokenizer.tokenizeFromString("5"); + const result = parser(tokenGenerator); + + assert.assertEquals( + result, + [ + { kind: "token", token: { kind: "T_NUMERIC", value: "5" } }, + ], + ); +}); + +Deno.test("sequence", () => { + const parser = sequence( + token("T_NUMERIC"), + token("T_WHITESPACE"), + token("T_PLUS"), + token("T_WHITESPACE"), + token("T_NUMERIC"), + ); + + const tokenizer = new Tokenizer(createTokenizerState(simpleTokens)); + const tokenGenerator = tokenizer.tokenizeFromString("1 + 2 * 3"); + const result = parser(tokenGenerator); + + assert.assertEquals( + result, + [ + { kind: "token", token: { kind: "T_NUMERIC", value: "1" } }, + { kind: "token", token: { kind: "T_WHITESPACE", value: " " } }, + { kind: "token", token: { kind: "T_PLUS", value: "+" } }, + { kind: "token", token: { kind: "T_WHITESPACE", value: " " } }, + { kind: "token", token: { kind: "T_NUMERIC", value: "2" } }, + ], + ); +}); diff --git a/parsing/parser.ts b/pkg/parsing/parser.ts similarity index 100% rename from parsing/parser.ts rename to pkg/parsing/parser.ts diff --git a/parsing/rewindable.ts b/pkg/parsing/rewindable.ts similarity index 94% rename from parsing/rewindable.ts rename to pkg/parsing/rewindable.ts index 5f7c95dc..9b2ad2e6 100644 --- a/parsing/rewindable.ts +++ b/pkg/parsing/rewindable.ts @@ -35,7 +35,7 @@ export const rewindable = ( } const next = iterator.next(); - stash.push(next); + stash = [...stash, next]; if (next.done) { done = true; @@ -50,10 +50,10 @@ export const rewindable = ( done = false; buffer = [...stash]; - stash = []; + stash.splice(0); }, sweep: () => { - stash = []; + stash.splice(0); }, [Symbol.iterator]: function* () { let result = this.next(); diff --git a/standards/README.md b/pkg/standards/README.md similarity index 84% rename from standards/README.md rename to pkg/standards/README.md index 27cc68aa..73c48107 100644 --- a/standards/README.md +++ b/pkg/standards/README.md @@ -1,6 +1,6 @@ -# 📑 [cool/standards](./) +# 📑 [@eser/standards](./) -`cool/standards` provides common interfaces and type declarations for mostly +`@eser/standards` provides common interfaces and type declarations for mostly used modules for applications. It enables using implementations of these definitions without any tightly coupled dependency just by accessing them from a dependency injection container. diff --git a/pkg/standards/deno.jsonc b/pkg/standards/deno.jsonc new file mode 100644 index 00000000..0802cedc --- /dev/null +++ b/pkg/standards/deno.jsonc @@ -0,0 +1,15 @@ +{ + "name": "@eser/standards", + "version": "0.7.20", + "imports": {}, + "exports": { + ".": "./mod.ts", + "./functions": "./functions.ts", + "./js-runtime": "./js-runtime.ts", + "./logging": "./logging.ts", + "./patterns": "./patterns.ts", + "./promises": "./promises.ts", + "./run-modes": "./run-modes.ts", + "./utilities": "./utilities.ts" + } +} diff --git a/standards/functions.ts b/pkg/standards/functions.ts similarity index 54% rename from standards/functions.ts rename to pkg/standards/functions.ts index 3f0ee7a5..28db8872 100644 --- a/standards/functions.ts +++ b/pkg/standards/functions.ts @@ -1,11 +1,8 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -// deno-lint-ignore no-explicit-any -export type ArgList = ReadonlyArray; -// deno-lint-ignore no-explicit-any -export type GenericFunction = (...args: ArgList) => T; -// deno-lint-ignore no-explicit-any -export type GenericClass = new (...args: ArgList) => T; +export type ArgList = ReadonlyArray; +export type GenericFunction = (...args: ArgList) => TR; +export type GenericClass = new (...args: ArgList) => TC; export const nullAsyncGeneratorFn = async function* (): AsyncGenerator< TR @@ -15,8 +12,8 @@ export const AsyncGeneratorFunction = nullAsyncGeneratorFn.constructor; export const nullGeneratorFn = function* (): Generator {}; export const GeneratorFunction = nullGeneratorFn.constructor; -export const nullAsyncFn = async function (): Promise {}; +export const nullAsyncFn = async (): Promise => {}; export const AsyncFunction = nullAsyncFn.constructor; -export const nullFn = function (): TR | void {}; +export const nullFn = (): TR | void => {}; export const Function = globalThis.Function; diff --git a/pkg/standards/js-runtime.ts b/pkg/standards/js-runtime.ts new file mode 100644 index 00000000..232c3115 --- /dev/null +++ b/pkg/standards/js-runtime.ts @@ -0,0 +1,146 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +export const KnownJsRuntimes = { + Unknown: 0, + Deno: 1, +} as const; + +export type KnownJsRuntimeKey = Exclude< + keyof typeof KnownJsRuntimes, + number +>; +export type KnownJsRuntime = typeof KnownJsRuntimes[KnownJsRuntimeKey]; + +export const notImplemented = (name: string): () => never => { + return () => { + throw new Error(`This feature is not implemented: ${name}`); + }; +}; + +export const notSupportedJsRuntime = (name: string): () => never => { + return () => { + throw new Error(`JavaScript Runtime is not supported: ${name}`); + }; +}; + +export type JsRuntime = { + jsRuntime: KnownJsRuntime; + version: string; + + errors: { + // deno-lint-ignore no-explicit-any + NotFound: any; + }; + + execPath(): string; + getArgs(): Array; + getEnv(): { [index: string]: string }; + getMainModule(): string | undefined; + + getStdin(): ReadableStream; + getStdout(): WritableStream; + getStderr(): WritableStream; + + open(path: string | URL, options?: Deno.OpenOptions): Promise; + stat(path: string | URL): Promise; + exit(code?: number): never; + + readFile( + path: string | URL, + options?: Deno.ReadFileOptions, + ): Promise; + readTextFile( + path: string | URL, + options?: Deno.ReadFileOptions, + ): Promise; + writeFile( + path: string | URL, + data: Uint8Array | ReadableStream, + options?: Deno.WriteFileOptions, + ): Promise; + writeTextFile( + path: string | URL, + data: string | ReadableStream, + options?: Deno.WriteFileOptions, + ): Promise; + + openKv(path?: string): Promise; + Command: typeof Deno.Command; +}; + +export const createDenoJsRuntime = (): JsRuntime => { + const denoObjRef = globalThis.Deno; + + const instance = { + jsRuntime: KnownJsRuntimes.Deno, + version: denoObjRef.version.deno, + + errors: { + NotFound: denoObjRef.errors.NotFound, + }, + + execPath: denoObjRef.execPath, + getArgs: () => denoObjRef.args, + getEnv: () => denoObjRef.env.toObject(), + getMainModule: () => denoObjRef.mainModule, + + getStdin: () => denoObjRef.stdin.readable, + getStdout: () => denoObjRef.stdout.writable, + getStderr: () => denoObjRef.stderr.writable, + + open: denoObjRef.open, + stat: denoObjRef.stat, + exit: denoObjRef.exit, + + readFile: denoObjRef.readFile, + readTextFile: denoObjRef.readTextFile, + writeFile: denoObjRef.writeFile, + writeTextFile: denoObjRef.writeTextFile, + + openKv: denoObjRef.openKv ?? notImplemented("openKv"), + Command: denoObjRef.Command, + }; + + return instance; +}; + +export const createGenericJsRuntime = (): JsRuntime => { + return { + jsRuntime: KnownJsRuntimes.Unknown, + errors: { + NotFound: Error, + }, + + execPath: notImplemented("execPath"), + getArgs: notImplemented("getArgs"), + getEnv: notImplemented("getEnv"), + getMainModule: notImplemented("getMainModule"), + + getStdin: notImplemented("getStdin"), + getStdout: notImplemented("getStdout"), + getStderr: notImplemented("getStderr"), + + open: notImplemented("open"), + stat: notImplemented("stat"), + exit: notImplemented("exit"), + + readFile: notImplemented("readFile"), + readTextFile: notImplemented("readTextFile"), + writeFile: notImplemented("writeFile"), + writeTextFile: notImplemented("writeTextFile"), + + openKv: notImplemented("openKv"), + // @ts-expect-error Command is not implemented + Command: notImplemented("Command"), + }; +}; + +export const createJsRuntime = (): JsRuntime => { + if (globalThis.Deno !== undefined) { + return createDenoJsRuntime(); + } + + return createGenericJsRuntime(); +}; + +export const current: JsRuntime = createJsRuntime(); diff --git a/standards/logging.ts b/pkg/standards/logging.ts similarity index 83% rename from standards/logging.ts rename to pkg/standards/logging.ts index e83d2f26..8a7eeaa9 100644 --- a/standards/logging.ts +++ b/pkg/standards/logging.ts @@ -31,7 +31,11 @@ export const SeverityNames = { export interface Logger { log( severity: Severity, - message: (T extends functions.GenericFunction ? never : T) | (() => T), - ...args: functions.ArgList + message: + // deno-lint-ignore no-explicit-any + | (T extends functions.GenericFunction ? never : T) + | (() => T), + // deno-lint-ignore no-explicit-any + ...args: functions.ArgList ): Promise; } diff --git a/standards/mod.ts b/pkg/standards/mod.ts similarity index 77% rename from standards/mod.ts rename to pkg/standards/mod.ts index 1f85faf2..e0412117 100644 --- a/standards/mod.ts +++ b/pkg/standards/mod.ts @@ -5,4 +5,5 @@ export * as logging from "./logging.ts"; export * as patters from "./patterns.ts"; export * as promises from "./promises.ts"; export * as runModes from "./run-modes.ts"; -export * as runtime from "./runtime.ts"; +export * as jsRuntime from "./js-runtime.ts"; +export * as utilities from "./utilities.ts"; diff --git a/standards/patterns.ts b/pkg/standards/patterns.ts similarity index 100% rename from standards/patterns.ts rename to pkg/standards/patterns.ts diff --git a/standards/promises.ts b/pkg/standards/promises.ts similarity index 100% rename from standards/promises.ts rename to pkg/standards/promises.ts diff --git a/standards/run-modes.ts b/pkg/standards/run-modes.ts similarity index 100% rename from standards/run-modes.ts rename to pkg/standards/run-modes.ts diff --git a/pkg/standards/utilities.ts b/pkg/standards/utilities.ts new file mode 100644 index 00000000..ee928bee --- /dev/null +++ b/pkg/standards/utilities.ts @@ -0,0 +1,28 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +// deno-lint-ignore no-explicit-any +export type First> = T[0]; + +// deno-lint-ignore no-explicit-any +export type Last> = T extends + [...infer _Rest, infer Last] ? Last + : never; + +// deno-lint-ignore no-explicit-any +export type RemainingParameters = + // deno-lint-ignore no-explicit-any + T2 extends [any, ...infer T2Tail] + // deno-lint-ignore no-explicit-any + ? T1 extends [any, ...infer T1Tail] ? RemainingParameters + : [] + : T1; + +// deno-lint-ignore no-explicit-any +export type RemainingParametersRight = + // deno-lint-ignore no-explicit-any + T2 extends [any, ...infer T2Tail] + // deno-lint-ignore no-explicit-any + ? T1 extends [...infer T1Tail, any] + ? RemainingParametersRight + : [] + : T1; diff --git a/pre-commit.ts b/pre-commit.ts deleted file mode 100644 index 5e3ca513..00000000 --- a/pre-commit.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -import { jsonc, posix } from "./deps.ts"; -import * as runtime from "./standards/runtime.ts"; - -const main = async () => { - const baseUrl = new URL(".", import.meta.url); - const basePath = posix.fromFileUrl(baseUrl.href); - - const config = jsonc.parse( - await runtime.current.readTextFile(`${basePath}/deno.jsonc`), - ) as { version?: string }; - - await runtime.current.writeTextFile( - `${basePath}/version.txt`, - `${config.version ?? "0.0.0"}\n`, - ); -}; - -main(); diff --git a/scripts/deno.jsonc b/scripts/deno.jsonc new file mode 100644 index 00000000..e1b4eaec --- /dev/null +++ b/scripts/deno.jsonc @@ -0,0 +1,7 @@ +{ + "imports": { + "@std/fs": "jsr:@std/fs@^0.229.3", + "@std/jsonc": "jsr:@std/jsonc@^0.224.3", + "@std/path": "jsr:@std/path@^1.0.0" + } +} diff --git a/repl-init.ts b/scripts/repl-init.ts similarity index 78% rename from repl-init.ts rename to scripts/repl-init.ts index f707a55b..729e35a3 100644 --- a/repl-init.ts +++ b/scripts/repl-init.ts @@ -1,16 +1,16 @@ // Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. -import * as dotenv from "./dotenv/mod.ts"; -import * as runtime from "./standards/runtime.ts"; -import * as mod from "./mod.ts"; +import * as config from "@eser/config"; +import * as jsRuntime from "@eser/standards/js-runtime"; +// import * as mod from "@eser/"; // TODO(@eser) get dependency injection container entries instead of this await (async () => { - const env = await dotenv.load(); - const kv = await runtime.current.openKv(); + const env = await config.dotenv.load(); + const kv = await jsRuntime.current.openKv(); const variables: Record = { - ...mod, + // ...mod, env, kv, }; diff --git a/scripts/validate-configs.ts b/scripts/validate-configs.ts new file mode 100644 index 00000000..81c3c6f9 --- /dev/null +++ b/scripts/validate-configs.ts @@ -0,0 +1,73 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. + +import * as jsonc from "@std/jsonc"; +import * as posix from "@std/path/posix"; +// import * as walk from "@std/fs/walk"; + +import * as jsRuntime from "@eser/standards/js-runtime"; + +type DenoConfig = { + name?: string; + version?: string; + workspace?: string[]; + imports: Record; + exports: Record; +}; + +const tryLoadJsonFile = async ( + path: string, +): Promise => { + try { + const config = jsonc.parse( + await jsRuntime.current.readTextFile(path), + { allowTrailingComma: true }, + ) as DenoConfig; + + return config; + } catch { + return undefined; + } +}; + +const main = async () => { + const baseUrl = new URL(".", import.meta.url); + const basePath = posix.join( + posix.fromFileUrl(baseUrl.href), + "..", + ); + + const rootConfig = await tryLoadJsonFile(`${basePath}/deno.jsonc`); + if (rootConfig === undefined) { + console.error("Could not load deno.jsonc"); + return; + } + + for (const entry of rootConfig.workspace ?? []) { + console.log(`Processing ${entry}...`); + const modulePath = posix.join(basePath, entry); + const moduleConfigFile = posix.join(modulePath, "deno.jsonc"); + + const moduleConfig = await tryLoadJsonFile(moduleConfigFile); + if (moduleConfig === undefined) { + console.error(` Could not load JSONC file`); + continue; + } + + if (moduleConfig.name === undefined) { + console.error(` Skipping this since module name is not specified`); + continue; + } + + // const moduleName = posix.basename(entry); + // moduleConfig.name = `@eser/${moduleName}`; + moduleConfig.version = rootConfig.version ?? "0.0.0"; + + const contents = `${JSON.stringify(moduleConfig, null, 2)}\n`; + await jsRuntime.current.writeTextFile( + `${modulePath}/deno.jsonc`, + contents, + ); + } +}; + +main(); diff --git a/scripts/validate-licenses.ts b/scripts/validate-licenses.ts new file mode 100644 index 00000000..3236923e --- /dev/null +++ b/scripts/validate-licenses.ts @@ -0,0 +1,83 @@ +// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. +// Copied from $std/_tools/check_license.ts + +import * as posix from "@std/path/posix"; +import * as walk from "@std/fs/walk"; +import * as jsRuntime from "@eser/standards/js-runtime"; + +const main = async () => { + const EXTENSIONS = ["*.js", ".ts", "*.jsx", ".tsx"]; + const EXCLUDES = [ + /_etc\/coverage\/*$/, + /_etc\/temp\/*$/, + /_etc\/templates\/*$/, + /manifest\.gen\.ts$/, + ]; + + const baseUrl = new URL(".", import.meta.url); + const basePath = posix.join( + posix.fromFileUrl(baseUrl.href), + "..", + ); + + const CHECK = jsRuntime.current.getArgs().includes("--check"); + const BASE_YEAR = "2023"; + // const CURRENT_YEAR = new Date().getFullYear(); + const RX_COPYRIGHT = new RegExp( + `// Copyright ([0-9]{4})-present Eser Ozvataf and other contributors\\. All rights reserved\\. ([0-9A-Za-z-.]+) license\\.\n`, + ); + const COPYRIGHT = + `// Copyright ${BASE_YEAR}-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license.`; + + let failed = false; + + for await ( + const entry of walk.walk(basePath, { + exts: EXTENSIONS, + skip: EXCLUDES, + includeDirs: false, + }) + ) { + const content = await jsRuntime.current.readTextFile(entry.path); + const match = content.match(RX_COPYRIGHT); + + if (match) { + if (match[1] === BASE_YEAR) { + // everything is fine + continue; + } + + if (CHECK) { + console.error(`Incorrect copyright year: ${entry.path}`); + failed = true; + continue; + } + + const index = match.index ?? 0; + const contentWithoutCopyright = content.replace(match[0], ""); + const contentWithCopyright = contentWithoutCopyright.substring(0, index) + + COPYRIGHT + "\n" + contentWithoutCopyright.substring(index); + await jsRuntime.current.writeTextFile(entry.path, contentWithCopyright); + console.log("Copyright header automatically updated in " + entry.path); + + continue; + } + + if (CHECK) { + console.error(`Missing copyright header: ${entry.path}`); + failed = true; + continue; + } + + const contentWithCopyright = COPYRIGHT + "\n" + content; + await jsRuntime.current.writeTextFile(entry.path, contentWithCopyright); + console.log("Copyright header automatically added to " + entry.path); + } + + if (failed) { + console.info(`Copyright header should be "${COPYRIGHT}"`); + jsRuntime.current.exit(1); + } +}; + +main(); diff --git a/standards/runtime.ts b/standards/runtime.ts deleted file mode 100644 index 9e6fa60a..00000000 --- a/standards/runtime.ts +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license. - -export const SupportedRuntimes = { - Unknown: 0, - Deno: 1, -} as const; - -export type SupportedRuntimeKey = Exclude< - keyof typeof SupportedRuntimes, - number ->; -export type SupportedRuntime = typeof SupportedRuntimes[SupportedRuntimeKey]; - -export const notImplemented = (name: string): () => never => { - return () => { - throw new Error(`This feature is not implemented: ${name}`); - }; -}; - -export const notSupportedRuntime = (name: string): () => never => { - return () => { - throw new Error(`Runtime is not supported: ${name}`); - }; -}; - -export type Runtime = { - runtime: SupportedRuntime; - - errors: { - // deno-lint-ignore no-explicit-any - NotFound: any; - }; - - execPath(): string; - getArgs(): Array; - getEnv(): { [index: string]: string }; - getMainModule(): string | undefined; - - getStdin(): ReadableStream; - getStdout(): WritableStream; - getStderr(): WritableStream; - - open(path: string | URL, options?: Deno.OpenOptions): Promise; - stat(path: string | URL): Promise; - exit(code?: number): never; - - readFile( - path: string | URL, - options?: Deno.ReadFileOptions, - ): Promise; - readTextFile( - path: string | URL, - options?: Deno.ReadFileOptions, - ): Promise; - writeFile( - path: string | URL, - data: Uint8Array | ReadableStream, - options?: Deno.WriteFileOptions, - ): Promise; - writeTextFile( - path: string | URL, - data: string | ReadableStream, - options?: Deno.WriteFileOptions, - ): Promise; - - openKv(path?: string): Promise; - Command: typeof Deno.Command; -}; - -export const createDenoRuntime = (): Runtime => { - const denoObjRef = globalThis.Deno; - - const instance = { - runtime: SupportedRuntimes.Deno, - errors: { - NotFound: denoObjRef.errors.NotFound, - }, - - execPath: denoObjRef.execPath, - getArgs: () => denoObjRef.args, - getEnv: () => denoObjRef.env.toObject(), - getMainModule: () => denoObjRef.mainModule, - - getStdin: () => denoObjRef.stdin.readable, - getStdout: () => denoObjRef.stdout.writable, - getStderr: () => denoObjRef.stderr.writable, - - open: denoObjRef.open, - stat: denoObjRef.stat, - exit: denoObjRef.exit, - - readFile: denoObjRef.readFile, - readTextFile: denoObjRef.readTextFile, - writeFile: denoObjRef.writeFile, - writeTextFile: denoObjRef.writeTextFile, - - openKv: denoObjRef.openKv ?? notImplemented("openKv"), - Command: denoObjRef.Command, - }; - - return instance; -}; - -export const createGenericRuntime = (): Runtime => { - return { - runtime: SupportedRuntimes.Unknown, - errors: { - NotFound: Error, - }, - - execPath: notImplemented("execPath"), - getArgs: notImplemented("getArgs"), - getEnv: notImplemented("getEnv"), - getMainModule: notImplemented("getMainModule"), - - getStdin: notImplemented("getStdin"), - getStdout: notImplemented("getStdout"), - getStderr: notImplemented("getStderr"), - - open: notImplemented("open"), - stat: notImplemented("stat"), - exit: notImplemented("exit"), - - readFile: notImplemented("readFile"), - readTextFile: notImplemented("readTextFile"), - writeFile: notImplemented("writeFile"), - writeTextFile: notImplemented("writeTextFile"), - - openKv: notImplemented("openKv"), - // @ts-expect-error Command is not implemented - Command: notImplemented("Command"), - }; -}; - -export const createRuntime = (): Runtime => { - if (globalThis.Deno !== undefined) { - return createDenoRuntime(); - } - - return createGenericRuntime(); -}; - -export const current: Runtime = createRuntime(); - -// export const addSignalListener = denoObjRef?.addSignalListener ?? -// notImplemented; -// // export const args: string[] = denoObjRef?.args ?? []; // FIXME(@eser) throw error if undefined? -// export const bench = denoObjRef?.bench ?? notImplemented; -// // export const build: typeof Deno.build = denoObjRef?.build ?? {}; -// export const chdir = denoObjRef?.chdir ?? notImplemented; -// // export const ChildProcess = denoObjRef?.ChildProcess; -// // export const chmod = denoObjRef?.chmod ?? notImplemented; -// // export const chown = denoObjRef?.chown ?? notImplemented; -// // export const close = denoObjRef?.close ?? notImplemented; -// // export const Command = denoObjRef?.Command ?? notImplemented; -// // export const connect = denoObjRef?.connect ?? notImplemented; -// // export const connectTls = denoObjRef?.connectTls ?? notImplemented; -// // export const consoleSize = denoObjRef?.consoleSize ?? notImplemented; -// // export const copyFile = denoObjRef?.copyFile ?? notImplemented; -// // export const create = denoObjRef?.create ?? notImplemented; -// // export const cwd = denoObjRef?.cwd ?? notImplemented; -// // export const env: Deno.Env = denoObjRef?.env; -// // export const errors = denoObjRef?.errors; -// // export const execPath = denoObjRef?.execPath; -// // export const fdatasync = denoObjRef?.fdatasync ?? notImplemented; -// // export const FsFile = denoObjRef?.FsFile; -// // export const fstat = denoObjRef?.fstat ?? notImplemented; -// // export const fsync = denoObjRef?.fsync ?? notImplemented; -// // export const ftruncate = denoObjRef?.ftruncate ?? notImplemented; -// // export const futime = denoObjRef?.futime ?? notImplemented; -// // export const gid = denoObjRef?.gid ?? notImplemented; -// // export const hostname = denoObjRef?.hostname ?? notImplemented; -// // export const inspect = denoObjRef?.inspect ?? notImplemented; -// // export const isatty = denoObjRef?.isatty ?? notImplemented; -// // export const kill = denoObjRef?.kill ?? notImplemented; -// // export const link = denoObjRef?.link ?? notImplemented; -// // export const listen = denoObjRef?.listen ?? notImplemented; -// // export const listenTls = denoObjRef?.listenTls ?? notImplemented; -// // export const loadavg = denoObjRef?.loadavg ?? notImplemented; -// // export const lstat = denoObjRef?.lstat ?? notImplemented; -// // export const mainModule = denoObj?.permissions.querySync({ name: "read" }) -// // ? denoObj?.mainModule -// // : undefined; -// // export const makeTempDir = denoObjRef?.makeTempDir ?? notImplemented; -// // export const makeTempFile = denoObjRef?.makeTempFile ?? notImplemented; -// // export const memoryUsage = denoObjRef?.memoryUsage ?? notImplemented; -// // export const mkdir = denoObjRef?.mkdir ?? notImplemented; -// // export const networkInterfaces = denoObjRef?.networkInterfaces ?? notImplemented; -// // export const noColor = denoObjRef?.noColor; -// // export const open = denoObjRef?.open ?? notImplemented; -// // export const osRelease = denoObjRef?.osRelease ?? notImplemented; -// // export const osUptime = denoObjRef?.osUptime ?? notImplemented; -// // export const permissions: Deno.Permissions = denoObjRef?.permissions; -// // export const Permissions = denoObjRef?.Permissions; -// // export const PermissionStatus = denoObjRef?.PermissionStatus; -// // export const pid = denoObjRef?.pid; -// // export const ppid = denoObjRef?.ppid; -// // export const read = denoObjRef?.read ?? notImplemented; -// // export const readDir = denoObjRef?.readDir ?? notImplemented; -// // export const readFile = denoObjRef?.readFile ?? notImplemented; -// // export const readLink = denoObjRef?.readLink ?? notImplemented; -// export const readTextFile = denoObjRef?.readTextFile ?? notImplemented; -// // export const realPath = denoObjRef?.realPath ?? notImplemented; -// // export const refTimer = denoObjRef?.refTimer ?? notImplemented; -// // export const remove = denoObjRef?.remove ?? notImplemented; -// // export const removeSignalListener = denoObjRef?.removeSignalListener ?? notImplemented; -// // export const rename = denoObjRef?.rename ?? notImplemented; -// // export const resolveDns = denoObjRef?.resolveDns ?? notImplemented; -// // export const resources = denoObjRef?.resources ?? notImplemented; -// // export const seek = denoObjRef?.seek ?? notImplemented; -// // export const SeekMode = denoObjRef?.SeekMode; -// // export const serve = denoObjRef?.serve ?? notImplemented; -// // export const serveHttp = denoObjRef?.serveHttp ?? notImplemented; -// // export const shutdown = denoObjRef?.shutdown ?? notImplemented; -// // export const startTls = denoObjRef?.startTls ?? notImplemented; -// export const stat = denoObjRef?.stat ?? notImplemented; -// // export const stderr = denoObjRef?.stderr; -// // export const stdin = denoObjRef?.stdin; -// // export const stdout = denoObjRef?.stdout; -// // export const symlink = denoObjRef?.symlink ?? notImplemented; -// // export const systemMemoryInfo = denoObjRef?.systemMemoryInfo ?? notImplemented; -// // export const test = denoObjRef?.test ?? notImplemented; -// // export const truncate = denoObjRef?.truncate ?? notImplemented; -// // export const uid = denoObjRef?.uid ?? notImplemented; -// // export const unrefTimer = denoObjRef?.unrefTimer ?? notImplemented; -// // export const upgradeWebSocket = denoObjRef?.upgradeWebSocket ?? notImplemented; -// // export const utime = denoObjRef?.utime ?? notImplemented; -// // export const version = { ...denoObjRef?.version, runtime: denoObjRef?.version.deno }; -// // export const watchFs = denoObjRef?.watchFs ?? notImplemented; -// // export const write = denoObjRef?.write ?? notImplemented; -// // export const writeFile = denoObjRef?.writeFile ?? notImplemented; diff --git a/version.txt b/version.txt deleted file mode 100644 index b22af294..00000000 --- a/version.txt +++ /dev/null @@ -1 +0,0 @@ -0.7.17