diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index dd84ea7..b5c68e5 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -4,7 +4,6 @@ about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
-
---
**Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
+
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
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]
+
+- OS: [e.g. iOS]
+- Browser [e.g. chrome, safari]
+- Version [e.g. 22]
**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
+
+- Device: [e.g. iPhone6]
+- OS: [e.g. iOS8.1]
+- Browser [e.g. stock browser, safari]
+- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..cbe842a
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "semi": false,
+ "singleQuote": true
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index e221d2b..6858a77 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,53 +1,52 @@
{
- "version": "0.2.0",
- "configurations": [
- {
- "type": "bun",
- "request": "launch",
- "name": "Debug Bun",
-
- // The path to a JavaScript or TypeScript file to run.
- "program": "${file}",
-
- // The arguments to pass to the program, if any.
- "args": [],
-
- // The working directory of the program.
- "cwd": "${workspaceFolder}",
-
- // The environment variables to pass to the program.
- "env": {},
-
- // If the environment variables should not be inherited from the parent process.
- "strictEnv": false,
-
- // If the program should be run in watch mode.
- // This is equivalent to passing `--watch` to the `bun` executable.
- // You can also set this to "hot" to enable hot reloading using `--hot`.
- "watchMode": false,
-
- // If the debugger should stop on the first line of the program.
- "stopOnEntry": false,
-
- // If the debugger should be disabled. (for example, breakpoints will not be hit)
- "noDebug": false,
-
- // The path to the `bun` executable, defaults to your `PATH` environment variable.
- "runtime": "bun",
-
- // The arguments to pass to the `bun` executable, if any.
- // Unlike `args`, these are passed to the executable itself, not the program.
- "runtimeArgs": [],
- },
- {
- "type": "bun",
- "request": "attach",
- "name": "Attach to Bun",
-
- // The URL of the WebSocket inspector to attach to.
- // This value can be retrieved by using `bun --inspect`.
- "url": "ws://localhost:6499/",
- }
- ]
- }
-
\ No newline at end of file
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "bun",
+ "request": "launch",
+ "name": "Debug Bun",
+
+ // The path to a JavaScript or TypeScript file to run.
+ "program": "${file}",
+
+ // The arguments to pass to the program, if any.
+ "args": [],
+
+ // The working directory of the program.
+ "cwd": "${workspaceFolder}",
+
+ // The environment variables to pass to the program.
+ "env": {},
+
+ // If the environment variables should not be inherited from the parent process.
+ "strictEnv": false,
+
+ // If the program should be run in watch mode.
+ // This is equivalent to passing `--watch` to the `bun` executable.
+ // You can also set this to "hot" to enable hot reloading using `--hot`.
+ "watchMode": false,
+
+ // If the debugger should stop on the first line of the program.
+ "stopOnEntry": false,
+
+ // If the debugger should be disabled. (for example, breakpoints will not be hit)
+ "noDebug": false,
+
+ // The path to the `bun` executable, defaults to your `PATH` environment variable.
+ "runtime": "bun",
+
+ // The arguments to pass to the `bun` executable, if any.
+ // Unlike `args`, these are passed to the executable itself, not the program.
+ "runtimeArgs": []
+ },
+ {
+ "type": "bun",
+ "request": "attach",
+ "name": "Attach to Bun",
+
+ // The URL of the WebSocket inspector to attach to.
+ // This value can be retrieved by using `bun --inspect`.
+ "url": "ws://localhost:6499/"
+ }
+ ]
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 964897f..4ab7d6d 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the
+- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
-* The use of sexualized language or imagery, and sexual attention or
+- The use of sexualized language or imagery, and sexual attention or
advances of any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
address, without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
+- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
+standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
diff --git a/CREATING_A_MODULE.md b/CREATING_A_MODULE.md
index 92bb051..207add6 100644
--- a/CREATING_A_MODULE.md
+++ b/CREATING_A_MODULE.md
@@ -5,49 +5,64 @@ Certainly! Let's create a README document to guide users through the process of
# Creating a Module in BNK (Bun Nookit)
## Overview
+
This guide provides step-by-step instructions on how to create a new module for the BNK framework. Whether you're adding functionality like OAuth integration or something entirely different, these guidelines will help align the module with BNK's design philosophy and standards to ensure consistency.
## Prerequisites
+
- Familiarity with TypeScript and BNK's core concepts.
- Understanding of the problem domain the module will address.
## Step 1: Research and Requirements Gathering
+
Before coding, understand the scope and requirements of the module. For instance, if you're building an OAuth module, research the OAuth 2.0 protocol, and identify the primary use cases you want to support.
## Step 2: Designing the Module
+
### 2.1 Define the API
+
Design a clear and intuitive API for the module. Consider the functions and interfaces users will interact with.
### 2.2 Plan the Architecture
+
Ensure the module aligns with BNK's architecture. Use factory functions, avoid global state, and adhere to strong typing.
### 2.3 Security and Performance
+
Plan for security and performance from the start. This is especially important for modules handling sensitive data or requiring high efficiency.
## Step 3: Implementation
+
### 3.1 Setup
+
Set up the basic structure of the module. Create a new directory and files as needed within the BNK project structure.
### 3.2 Core Functionality
+
Develop the core functionality of the module. Keep functions short and focused, and use descriptive names.
### 3.3 Integration
+
Ensure that the module integrates seamlessly with other BNK components.
### 3.4 Error Handling
+
Implement robust error handling to make the module resilient and reliable.
## Step 4: Testing
+
Write comprehensive tests for the module. Cover unit testing for individual functions and integration testing for the module as a whole.
## Step 5: Documentation
+
Document the module thoroughly. Include a usage guide, example implementations, and a detailed API reference.
## Step 6: Community Feedback and Iteration
+
Release a beta version of the module and encourage feedback from the BNK community. Iterate based on the feedback received.
## Best Practices
+
- **Follow BNK's Coding Style**: Adhere to the principles outlined in BNK's coding guidelines, such as using `const`, writing pure functions, and avoiding premature optimization.
- **Use Descriptive Names**: Choose clear and descriptive names for functions, variables, and modules.
- **Write Efficient Code**: Focus on practicality and optimization where necessary. Prioritize clarity and simplicity.
-
diff --git a/README.md b/README.md
index 55cdfa2..5a7041a 100644
--- a/README.md
+++ b/README.md
@@ -4,16 +4,15 @@
**Bun Nookit (BNK)** is a comprehensive toolkit for software development, leveraging the power of Bun and TypeScript. With zero third-party dependencies, strong TypeScript inferencing, and a focus on Web API standards, BNK offers a modular, type-safe, and efficient way to build robust applications.
-
![GitHub License](https://img.shields.io/github/license/nookit-dev/bnkit)
-![npm](https://img.shields.io/npm/v/bnkit?logo=npm) ![GitHub release (with filter)](https://img.shields.io/github/v/release/nookit-dev/bnkit) ![Stars](https://img.shields.io/github/stars/nookit-dev/bnkit)
-![npm bundle size](https://img.shields.io/bundlephobia/min/bnkit) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/bnkit)
+![npm](https://img.shields.io/npm/v/bnkit?logo=npm) ![GitHub release (with filter)](https://img.shields.io/github/v/release/nookit-dev/bnkit) ![Stars](https://img.shields.io/github/stars/nookit-dev/bnkit)
+![npm bundle size](https://img.shields.io/bundlephobia/min/bnkit) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/bnkit)
-![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/bun_nook_kit). ![Discord](https://img.shields.io/discord/1164699087543746560)'
+![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/bun_nook_kit). ![Discord](https://img.shields.io/discord/1164699087543746560)'
+## BNK Server Quickstart
-## BNK Server Quickstart
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/nookit-dev/bnkit/main/scripts/quickstart.sh)
```
@@ -22,21 +21,25 @@ Visit `http://localhost:3000` in your browser and you should see Hello world and
`http://localhost:3000/json` for the json
---
-###
+
+###
+
# [📋 Documentation](https://nookit.dev/readme)
+
#### [🧩 Modules Docs](https://nookit.dev/modules)
+
#### [🖥️ BNK CLI Docs](https://nookit.dev/bnk-cli/bnk-cli-readme)
-#### [🔌 Plugin Docs](https://nookit.dev/plugins/BNK+Plugins)
-###
+#### [🔌 Plugin Docs](https://nookit.dev/plugins/BNK+Plugins)
+###
## Bun Nookit Package Installation
-Install in your project:
+Install in your project:
`bun add bnkit`
-Plugin install example:
+Plugin install example:
`bun add @bnk/react`
Use any an all Bun Nookit modules - server example with json response (similar to starter project)
@@ -44,26 +47,26 @@ Use any an all Bun Nookit modules - server example with json response (similar t
`index.ts`
```typescript
-import { jsonRes, serverFactory } from "bnkit/server";
-import { middleware, RoutesWithMiddleware } from "./middlewares";
+import { jsonRes, serverFactory } from 'bnkit/server'
+import { middleware, RoutesWithMiddleware } from './middlewares'
const routes = {
- "/": {
+ '/': {
// parse from request if neeeded
- get: (request) => new Response("Hello World!")
+ get: (request) => new Response('Hello World!'),
+ },
+ '/json': {
+ get: (request) =>
+ bnk.server.jsonRes({
+ message: 'Hello JSON Response!',
+ }),
},
- "/json": {
- get: request => bnk.server.jsonRes({
- message: "Hello JSON Response!"
- })
- }
} satisfies RoutesWithMiddleware
const { start, routes } = bnk.server.serverFactory({
routes,
- middleware
-});
-
+ middleware,
+})
// start on default port 3000
start()
@@ -76,7 +79,6 @@ Join our [Discord Server]("https://discord.gg/rQyWN7V6") https://discord.gg/rQyW
## Key Highlights
- **Zero Third Paty Dependencies** - BNK uses nothin' but Bun
-
- **Unit Tested** - To ensure BNK is reliable, changeable, and upgradeable.
- **TypeSafe with Strong TypeScript type Inferencing** - Strong types tell you where things are incorrect, strong type inferrence allows you to utilize the advantages of strong types and not having to deal with too much TypeScript.
@@ -138,6 +140,7 @@ Close To Final For V1:
### Better handling for Server Sent Events in Server, Fetcher, etc
## Screenshots
+
(if you made it this far)
Create typesafe server routes and middleware!
@@ -146,8 +149,8 @@ Create typesafe server routes and middleware!
-
### Sponsors
+
None! Be the first to sponsor BNK :)
## License
@@ -157,4 +160,5 @@ Bun Nookit is licensed under the MIT License. Enjoy the freedom to use, modify,
Jumpstart your journey to revolutionary software development with Bun Nookit!
Contribute to the docs:
+
### [Docs Repo](https://github.com/nookit-dev/bnkit-docs)
diff --git a/auth/example/google-oauth-server-example.ts b/auth/example/google-oauth-server-example.ts
index e229f87..268f07c 100644
--- a/auth/example/google-oauth-server-example.ts
+++ b/auth/example/google-oauth-server-example.ts
@@ -1,68 +1,68 @@
-import { oAuthFactory } from "auth/oauth";
-import { initGoogleOAuth } from "auth/oauth-providers";
-import type { Routes } from "server";
-import { serverFactory } from "server";
+import { oAuthFactory } from 'auth/oauth'
+import { initGoogleOAuth } from 'auth/oauth-providers'
+import type { Routes } from 'server'
+import { serverFactory } from 'server'
-const googleClientId = Bun.env.GOOGLE_OAUTH_CLIENT_ID || "";
-const googleClientSecret = Bun.env.GOOGLE_OAUTH_CLIENT_SECRET || "";
+const googleClientId = Bun.env.GOOGLE_OAUTH_CLIENT_ID || ''
+const googleClientSecret = Bun.env.GOOGLE_OAUTH_CLIENT_SECRET || ''
const googleOAuthConfig = initGoogleOAuth({
clientId: googleClientId,
clientSecret: googleClientSecret,
-});
+})
-const googleOAuth = oAuthFactory(googleOAuthConfig);
+const googleOAuth = oAuthFactory(googleOAuthConfig)
const routes = {
- "/login": {
+ '/login': {
get: () => {
// you could pass a param for the provider
- const authUrl = googleOAuth.initiateOAuthFlow();
+ const authUrl = googleOAuth.initiateOAuthFlow()
return new Response(null, {
headers: { Location: authUrl },
status: 302,
- });
+ })
},
},
- "/callback": {
+ '/callback': {
get: async (req) => {
try {
- const host = req.headers.get("host");
+ const host = req.headers.get('host')
// Parse the URL and query parameters
- const url = new URL(req.url, `http://${host}`);
- const queryParams = new URLSearchParams(url.search);
- const code = queryParams.get("code");
+ const url = new URL(req.url, `http://${host}`)
+ const queryParams = new URLSearchParams(url.search)
+ const code = queryParams.get('code')
if (!code) {
- return new Response("No code provided in query", { status: 400 });
+ return new Response('No code provided in query', { status: 400 })
}
- const tokenInfo = await googleOAuth.handleRedirect(code);
+ const tokenInfo = await googleOAuth.handleRedirect(code)
- console.log({ tokenInfo });
+ console.log({ tokenInfo })
// Logic after successful authentication
- return new Response("Login Successful!");
+ return new Response('Login Successful!')
} catch (error) {
- console.error(error);
- return new Response("Authentication failed", { status: 403 });
+ console.error(error)
+ return new Response('Authentication failed', { status: 403 })
}
},
},
- "/": {
+ '/': {
get: () => {
// HTML content for the login page
- const htmlContent = `
Login with Google Login `;
+ const htmlContent = `Login with Google Login `
return new Response(htmlContent, {
- headers: { "Content-Type": "text/html" },
- });
+ headers: { 'Content-Type': 'text/html' },
+ })
},
},
-} satisfies Routes;
+} satisfies Routes
const server = serverFactory({
routes,
-});
+})
-server.start(3000);
+server.start(3000)
diff --git a/auth/example/setup-oauth-providers.md b/auth/example/setup-oauth-providers.md
index 85898d3..9b6ec2b 100644
--- a/auth/example/setup-oauth-providers.md
+++ b/auth/example/setup-oauth-providers.md
@@ -3,29 +3,35 @@
## Google OAuth Setup
#### Step 1: Create a Google Cloud Project
+
- **Access Google Cloud Console**: Go to [Google Cloud Console](https://console.cloud.google.com/).
- **New Project**: Click 'New Project', name it, and create.
#### Step 2: Configure OAuth Consent Screen
+
- **Credentials Page**: Navigate to 'Credentials' under 'APIs & Services'.
- **Consent Screen Setup**: Click 'Configure Consent Screen', select 'External', and create.
- **Details**: Enter app name, support email, and developer email. Add optional details like logo and policy links.
- **Save**: Click 'Save and Continue'.
#### Step 3: Create OAuth 2.0 Credentials
+
- **Credentials Creation**: Back on 'Credentials' page, select 'Create Credentials' > 'OAuth client ID'.
- **Application Type**: Choose 'Web application'.
- **Redirect URIs**: Add your redirect URI (/callback).
- **Client ID & Secret**: After clicking 'Create', note down the client ID and secret.
#### Step 4: Enable Required APIs
+
- **API Library**: In 'Library', search and enable needed Google APIs.
#### Step 5: Implement OAuth in Your App
+
- **Integrate Credentials**: Use client ID and secret in your app's OAuth config.
- **Handle Redirects**: Ensure handling of Google's redirects and token exchange.
#### Step 6: Test and Deploy
+
- **Testing**: Thoroughly test the OAuth flow.
- **Verification and Deployment**: Submit for verification if needed and deploy.
@@ -40,12 +46,10 @@ This guide provides a condensed overview of setting up Google OAuth. Adapt it ba
5. **Scopes**: Decide on the scopes you need, like `user:email` for email access.
6. **Callback URL**: Set your callback URL that GitHub will redirect to after authentication.
-
#### Don't Forget:
1. **Submit for Verification**: If your application will be used by users outside your organization, you must submit your OAuth consent screen for verification by Google.
-
## Meta (Facebook) OAuth
1. **Facebook App**: Create a new app in the Facebook Developer portal.
@@ -78,4 +82,3 @@ This guide provides a condensed overview of setting up Google OAuth. Adapt it ba
2. **Redirect to Authorization URL**: On your login page, add buttons for each service that redirects to their respective authorization URL with the necessary query parameters.
3. **Handle Callbacks**: Implement routes in your server to handle the callbacks, exchanging the authorization code for tokens.
4. **User Authentication**: Use the tokens to fetch user details and authenticate or register them in your system.
-
diff --git a/auth/index.ts b/auth/index.ts
index 6d7d8cb..3d3edff 100644
--- a/auth/index.ts
+++ b/auth/index.ts
@@ -1,10 +1,5 @@
-export {
- createSecurityToken,
- createToken,
- getTokenExpireEpoch,
- verifyToken,
-} from "./security-token";
+export { createSecurityToken, createToken, getTokenExpireEpoch, verifyToken } from './security-token'
-export { oAuthFactory } from "./oauth";
+export { oAuthFactory } from './oauth'
-export { initGoogleOAuth, oAuthProviders } from "./oauth-providers";
+export { initGoogleOAuth, oAuthProviders } from './oauth-providers'
diff --git a/auth/oauth-providers.ts b/auth/oauth-providers.ts
index b9d71c1..e480b64 100644
--- a/auth/oauth-providers.ts
+++ b/auth/oauth-providers.ts
@@ -1,41 +1,41 @@
-import { OAuthConfig, OAuthProviderFn } from "./oauth-types";
+import { OAuthConfig, OAuthProviderFn } from './oauth-types'
-export type ProvidersConfigRecord = Record>;
+export type ProvidersConfigRecord = Record>
export const oAuthProviders = {
google: {
- redirectUri: "http://localhost:3000/callback", // just a default placeholder
- authReqUrl: "https://accounts.google.com/o/oauth2/v2/auth",
- tokenUrl: "https://oauth2.googleapis.com/token",
+ redirectUri: 'http://localhost:3000/callback', // just a default placeholder
+ authReqUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
+ tokenUrl: 'https://oauth2.googleapis.com/token',
},
microsoft: {
- redirectUri: "http://localhost:3000/callback",
- authReqUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
- tokenUrl: "http://needtofind",
+ redirectUri: 'http://localhost:3000/callback',
+ authReqUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
+ tokenUrl: 'http://needtofind',
},
github: {
- redirectUri: "http://localhost:3000/callback",
- authReqUrl: "https://github.com/login/oauth/authorize",
- tokenUrl: "http://https://github.com/login/oauth/access_token",
+ redirectUri: 'http://localhost:3000/callback',
+ authReqUrl: 'https://github.com/login/oauth/authorize',
+ tokenUrl: 'http://https://github.com/login/oauth/access_token',
},
-} satisfies ProvidersConfigRecord;
+} satisfies ProvidersConfigRecord
export const initGoogleOAuth: OAuthProviderFn = ({ clientId, clientSecret }, options) => {
- const redirectUrl = options?.redirectUrl;
+ const redirectUrl = options?.redirectUrl
return {
...oAuthProviders.google,
redirectUri: redirectUrl ? redirectUrl : oAuthProviders.google.redirectUri,
clientId,
clientSecret,
- };
-};
+ }
+}
export const initGithubOAuth: OAuthProviderFn = ({ clientId, clientSecret }, options) => {
- const redirectUrl = options?.redirectUrl;
+ const redirectUrl = options?.redirectUrl
return {
...oAuthProviders.github,
redirectUri: redirectUrl ? redirectUrl : oAuthProviders.github.redirectUri,
clientId,
clientSecret,
- };
-};
+ }
+}
diff --git a/auth/oauth-types.ts b/auth/oauth-types.ts
index 2888918..d21fc16 100644
--- a/auth/oauth-types.ts
+++ b/auth/oauth-types.ts
@@ -1,35 +1,35 @@
export type OAuthHelpers = {
- getAuthorizationUrl(config: OAuthConfig): string;
- getToken(code: string, config: OAuthConfig): Promise; // Simplified for demonstration
-};
+ getAuthorizationUrl(config: OAuthConfig): string
+ getToken(code: string, config: OAuthConfig): Promise // Simplified for demonstration
+}
export type OAuthConfig = {
- clientId: string;
- clientSecret: string;
+ clientId: string
+ clientSecret: string
// the server route that handles the redirect from the OAuth provider
- redirectUri: string;
+ redirectUri: string
// the url that handles the token request from the OAuth provider
- tokenUrl: string;
+ tokenUrl: string
// the server route that handles the token request from the OAuth provider
- authReqUrl: string;
- headers?: Record;
-};
+ authReqUrl: string
+ headers?: Record
+}
export type OAuthToken = {
- accessToken: string;
- tokenType: string;
- expiresIn: number; // Time in seconds after which the token expires
- refreshToken?: string; // Optional, not all flows return a refresh token
- scope?: string; // Optional, scope of the access granted
- idToken?: string; // Optional, used in OpenID Connect (OIDC)
+ accessToken: string
+ tokenType: string
+ expiresIn: number // Time in seconds after which the token expires
+ refreshToken?: string // Optional, not all flows return a refresh token
+ scope?: string // Optional, scope of the access granted
+ idToken?: string // Optional, used in OpenID Connect (OIDC)
// Additional fields can be added here depending on the OAuth provider
-};
+}
export type OAuthProviderOptions = {
- redirectUrl: string;
-};
+ redirectUrl: string
+}
-export type OAuthProviderCreds = Pick;
-export type OAuthProviderFn = (config: OAuthProviderCreds, options?: OAuthProviderOptions) => OAuthConfig;
+export type OAuthProviderCreds = Pick
+export type OAuthProviderFn = (config: OAuthProviderCreds, options?: OAuthProviderOptions) => OAuthConfig
-export type OAuthProviderInitializer = (config: OAuthConfig) => OAuthHelpers;
+export type OAuthProviderInitializer = (config: OAuthConfig) => OAuthHelpers
diff --git a/auth/oauth.ts b/auth/oauth.ts
index 3fb313a..cda7818 100644
--- a/auth/oauth.ts
+++ b/auth/oauth.ts
@@ -1,27 +1,27 @@
-import { OAuthConfig, OAuthProviderInitializer, OAuthToken } from "./oauth-types";
+import { OAuthConfig, OAuthProviderInitializer, OAuthToken } from './oauth-types'
type FetcherResponse = T & {
- error?: string;
-};
+ error?: string
+}
// Generic OAuth fetcher
export async function oAuthFetcher(
url: string,
options: {
- params: Record;
- headers?: Record;
+ params: Record
+ headers?: Record
},
): Promise> {
const response = await fetch(url, {
- method: "post",
+ method: 'post',
headers: {
...options.headers,
- "Content-Type": "application/x-www-form-urlencoded",
+ 'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(options.params).toString(),
- });
+ })
- return response.json();
+ return response.json()
}
// Wrapper function for getting token
@@ -29,66 +29,66 @@ export async function getOAuthToken({
code,
config: { clientId, clientSecret, redirectUri, tokenUrl },
}: {
- code: string;
- config: Omit;
+ code: string
+ config: Omit
}): Promise> {
const params = {
code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
- grant_type: "authorization_code",
- };
+ grant_type: 'authorization_code',
+ }
- return oAuthFetcher(tokenUrl, params);
+ return oAuthFetcher(tokenUrl, params)
}
export const initProvider: OAuthProviderInitializer = ({ clientId, authReqUrl, redirectUri, headers }) => {
return {
// TODO add options to be able to change response_type/scope, etc
getAuthorizationUrl: () => {
- const authUrl = authReqUrl;
+ const authUrl = authReqUrl
const queryParams = new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
- response_type: "code",
- scope: "email profile",
- });
+ response_type: 'code',
+ scope: 'email profile',
+ })
- return `${authUrl}?${queryParams.toString()}`;
+ return `${authUrl}?${queryParams.toString()}`
},
getToken: async (code: string) => {
return getOAuthToken({
code,
config: {
clientId,
- clientSecret: "",
+ clientSecret: '',
redirectUri,
- tokenUrl: "https://oauth2.googleapis.com/token",
+ tokenUrl: 'https://oauth2.googleapis.com/token',
headers,
},
}).then((response) => {
if (response.error) {
- console.error("Error fetching token:", response.error);
- throw new Error(response.error);
+ console.error('Error fetching token:', response.error)
+ throw new Error(response.error)
} else {
- console.log("Access Token:", response.accessToken);
- return response;
+ console.log('Access Token:', response.accessToken)
+ return response
}
- });
+ })
},
- };
-};
+ }
+}
export const oAuthFactory = (config: OAuthConfig) => {
- const provider = initProvider(config);
+ const provider = initProvider(config)
return {
handleRedirect: async (code: string) => {
- return await provider.getToken(code, config);
+ return await provider.getToken(code, config)
},
initiateOAuthFlow: () => {
- return provider.getAuthorizationUrl(config);
+ return provider.getAuthorizationUrl(config)
},
- };
-};
+ }
+}
diff --git a/auth/security-token.test.ts b/auth/security-token.test.ts
index 89e0d50..ec61b1e 100644
--- a/auth/security-token.test.ts
+++ b/auth/security-token.test.ts
@@ -1,52 +1,52 @@
-import { describe, it } from "bun:test";
-import { expect } from "bun:test";
-import { getTokenExpireEpoch, createToken, verifyToken, createSecurityToken } from ".";
+import { describe, it } from 'bun:test'
+import { expect } from 'bun:test'
+import { getTokenExpireEpoch, createToken, verifyToken, createSecurityToken } from '.'
-describe("Token Utilities", () => {
- describe("getTokenExpireEpoch", () => {
- it("should return the correct expiration epoch", () => {
- const date = new Date("2023-10-26T12:00:00Z");
- const tokenValidTimeSec = 3600; // 1 hour in seconds
- const expected = date.getTime() + tokenValidTimeSec * 1000; // convert seconds to milliseconds
- const result = getTokenExpireEpoch(date, tokenValidTimeSec);
+describe('Token Utilities', () => {
+ describe('getTokenExpireEpoch', () => {
+ it('should return the correct expiration epoch', () => {
+ const date = new Date('2023-10-26T12:00:00Z')
+ const tokenValidTimeSec = 3600 // 1 hour in seconds
+ const expected = date.getTime() + tokenValidTimeSec * 1000 // convert seconds to milliseconds
+ const result = getTokenExpireEpoch(date, tokenValidTimeSec)
- expect(result).toEqual(expected);
- });
- });
- describe("verifyToken", () => {
- it("should verify the token correctly", async () => {
- const salt = "randomSalt";
- const originalString = "testToken";
- const hashedToken = await createToken(originalString, salt);
- const isVerified = await verifyToken(originalString, salt, hashedToken);
- expect(isVerified).toBe(true);
- });
- });
+ expect(result).toEqual(expected)
+ })
+ })
+ describe('verifyToken', () => {
+ it('should verify the token correctly', async () => {
+ const salt = 'randomSalt'
+ const originalString = 'testToken'
+ const hashedToken = await createToken(originalString, salt)
+ const isVerified = await verifyToken(originalString, salt, hashedToken)
+ expect(isVerified).toBe(true)
+ })
+ })
- describe("createToken", () => {
- it("should create a hashed token", async () => {
- const salt = "randomSalt";
- const originalString = "testToken";
- const hashedToken = await createToken(originalString, salt);
- expect(hashedToken).toBeTruthy();
- expect(hashedToken).not.toEqual(originalString); // Ensure the hashed token is not the same as the original string
- });
- });
+ describe('createToken', () => {
+ it('should create a hashed token', async () => {
+ const salt = 'randomSalt'
+ const originalString = 'testToken'
+ const hashedToken = await createToken(originalString, salt)
+ expect(hashedToken).toBeTruthy()
+ expect(hashedToken).not.toEqual(originalString) // Ensure the hashed token is not the same as the original string
+ })
+ })
- describe("createSecurityToken", () => {
- it("should create a security token with default expiration time", async () => {
- const result = await createSecurityToken(5000);
- expect(result.securityToken).toBeTruthy();
- expect(result.tokenId).toBeTruthy();
- expect(result.tokenExpireEpoch).toBeGreaterThan(Date.now());
- });
+ describe('createSecurityToken', () => {
+ it('should create a security token with default expiration time', async () => {
+ const result = await createSecurityToken(5000)
+ expect(result.securityToken).toBeTruthy()
+ expect(result.tokenId).toBeTruthy()
+ expect(result.tokenExpireEpoch).toBeGreaterThan(Date.now())
+ })
- it("should create a security token with specified expiration time", async () => {
- const currentTime = new Date();
- const tokenValidTime = 60 * 15; // 15 minutes
- const result = await createSecurityToken(tokenValidTime, currentTime);
- const expectedExpiration = currentTime.getTime() + tokenValidTime * 1000;
- expect(result.tokenExpireEpoch).toBeCloseTo(expectedExpiration, -2); // -2 is for a precision of 10 milliseconds
- });
- });
-});
+ it('should create a security token with specified expiration time', async () => {
+ const currentTime = new Date()
+ const tokenValidTime = 60 * 15 // 15 minutes
+ const result = await createSecurityToken(tokenValidTime, currentTime)
+ const expectedExpiration = currentTime.getTime() + tokenValidTime * 1000
+ expect(result.tokenExpireEpoch).toBeCloseTo(expectedExpiration, -2) // -2 is for a precision of 10 milliseconds
+ })
+ })
+})
diff --git a/auth/security-token.ts b/auth/security-token.ts
index 5ec972d..b7d497b 100644
--- a/auth/security-token.ts
+++ b/auth/security-token.ts
@@ -1,40 +1,40 @@
-import { v7 as uuid } from "../uuid";
+import { v7 as uuid } from '../uuid'
export const getTokenExpireEpoch = (date: Date, tokenValidTimeSec: number) => {
- const expireEpoch = date.getTime() + tokenValidTimeSec * 1000;
+ const expireEpoch = date.getTime() + tokenValidTimeSec * 1000
- return expireEpoch;
-};
+ return expireEpoch
+}
export async function verifyToken(tokenString: string, salt: string, storedHash: string) {
- const fullPassword = tokenString + salt;
- const isMatch = await Bun.password.verify(fullPassword, storedHash);
+ const fullPassword = tokenString + salt
+ const isMatch = await Bun.password.verify(fullPassword, storedHash)
- return isMatch;
+ return isMatch
}
export async function createToken(string: string, salt: string) {
- const fullPassword = string + salt;
+ const fullPassword = string + salt
return await Bun.password.hash(fullPassword, {
- algorithm: "argon2id",
+ algorithm: 'argon2id',
memoryCost: 65536,
timeCost: 3,
- });
+ })
}
export const createSecurityToken = async (tokenValidTime: number, currentDate?: Date) => {
- const salt = uuid();
+ const salt = uuid()
const [tokenId, timestamp] = uuid({
returnTimestamp: true,
dateTime: currentDate,
- });
+ })
- const securityToken = await createToken(tokenId, salt);
- const tokenExpireEpoch = getTokenExpireEpoch(timestamp, tokenValidTime);
+ const securityToken = await createToken(tokenId, salt)
+ const tokenExpireEpoch = getTokenExpireEpoch(timestamp, tokenValidTime)
return {
securityToken,
tokenId,
tokenExpireEpoch,
- };
-};
+ }
+}
diff --git a/biome.json b/biome.json
deleted file mode 100644
index 56b1523..0000000
--- a/biome.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "https://biomejs.dev/schemas/1.4.0/schema.json",
- "organizeImports": {
- "enabled": false
- },
- "linter": {
- "enabled": true,
- "rules": {
- "recommended": true
- }
- },
- "formatter": {
- "enabled": true,
- "lineWidth": 120,
- "indentStyle": "space"
- }
-}
diff --git a/cli/cli-utils.test.ts b/cli/cli-utils.test.ts
index 72dc1f6..8e9e367 100644
--- a/cli/cli-utils.test.ts
+++ b/cli/cli-utils.test.ts
@@ -1,56 +1,56 @@
-import { describe, expect, test } from "bun:test";
-import { OptionDefinition, getOptionValue, parseArgument } from "./cli-utils";
+import { describe, expect, test } from 'bun:test'
+import { OptionDefinition, getOptionValue, parseArgument } from './cli-utils'
-describe("getOptionValue", () => {
- test("should return correct value for boolean type", () => {
- const arg = "--testArg";
- const nextArg = "true";
+describe('getOptionValue', () => {
+ test('should return correct value for boolean type', () => {
+ const arg = '--testArg'
+ const nextArg = 'true'
const optionDef: OptionDefinition = {
default: false,
- types: ["boolean"],
- };
+ types: ['boolean'],
+ }
- const value = getOptionValue(arg, nextArg, optionDef);
- expect(value).toBe(true);
- });
+ const value = getOptionValue(arg, nextArg, optionDef)
+ expect(value).toBe(true)
+ })
test("should return default value if nextArg starts with '--'", () => {
- const arg = "--testArg";
- const nextArg = "--anotherArg";
+ const arg = '--testArg'
+ const nextArg = '--anotherArg'
const optionDef: OptionDefinition = {
- default: "default",
- types: ["string"],
- };
+ default: 'default',
+ types: ['string'],
+ }
- const value = getOptionValue(arg, nextArg, optionDef);
- expect(value).toBe("default");
- });
-});
+ const value = getOptionValue(arg, nextArg, optionDef)
+ expect(value).toBe('default')
+ })
+})
-describe("parseArgument", () => {
- test("should return correct key and value", () => {
- const arg = "--testArg";
- const nextArg = "true";
+describe('parseArgument', () => {
+ test('should return correct key and value', () => {
+ const arg = '--testArg'
+ const nextArg = 'true'
- const { key, value } = parseArgument(arg, nextArg);
- expect(key).toBe("testArg");
- expect(value).toBe(true);
- });
+ const { key, value } = parseArgument(arg, nextArg)
+ expect(key).toBe('testArg')
+ expect(value).toBe(true)
+ })
test("should throw error if arg does not start with '--'", async () => {
- const arg = "testArg";
- const nextArg = "true";
+ const arg = 'testArg'
+ const nextArg = 'true'
- let error: Error | null = null;
+ let error: Error | null = null
try {
- parseArgument(arg, nextArg);
+ parseArgument(arg, nextArg)
} catch (e) {
if (e instanceof Error) {
- error = e;
+ error = e
}
}
- expect(error).toBeDefined();
- expect(error!.message).toBe(`Invalid parameter: ${arg}`);
- });
-});
+ expect(error).toBeDefined()
+ expect(error!.message).toBe(`Invalid parameter: ${arg}`)
+ })
+})
diff --git a/cli/cli-utils.ts b/cli/cli-utils.ts
index 98a6c8e..fdd6ea5 100644
--- a/cli/cli-utils.ts
+++ b/cli/cli-utils.ts
@@ -1,32 +1,32 @@
-import readline from "readline";
+import readline from 'readline'
const cliLog = (...args: any[]) => {
- console.info(...args);
-};
+ console.info(...args)
+}
// Get user input asynchronously
export async function getUserInput(): Promise {
- const proc = Bun.spawn([]);
- return await new Response(proc.stdout).text();
+ const proc = Bun.spawn([])
+ return await new Response(proc.stdout).text()
}
// Interface for parsed command line arguments
export interface ParsedArgs {
- [key: string]: string | boolean | undefined;
+ [key: string]: string | boolean | undefined
}
export interface OptionDefinition {
- default?: string | boolean;
- types: (string | boolean)[];
+ default?: string | boolean
+ types: (string | boolean)[]
}
const optionDefinitions: { [key: string]: OptionDefinition } = {
// Define available options here
-};
+}
// Parse command line arguments
export function getArguments(): string[] {
- return process.argv.slice(2);
+ return process.argv.slice(2)
}
export function getOptionValue(
@@ -34,66 +34,66 @@ export function getOptionValue(
nextArg: string,
optionDef: OptionDefinition,
): string | boolean | undefined {
- let value = optionDef.default;
-
- if (nextArg && !nextArg.startsWith("--")) {
- const type = optionDef.types.find((type) => type === typeof nextArg || type === typeof value);
- cliLog("Type found: ", type); // Debug log
- if (type === "boolean") {
- if (nextArg.toLowerCase() === "true") {
- value = true;
- } else if (nextArg.toLowerCase() === "false") {
- value = false;
+ let value = optionDef.default
+
+ if (nextArg && !nextArg.startsWith('--')) {
+ const type = optionDef.types.find((type) => type === typeof nextArg || type === typeof value)
+ cliLog('Type found: ', type) // Debug log
+ if (type === 'boolean') {
+ if (nextArg.toLowerCase() === 'true') {
+ value = true
+ } else if (nextArg.toLowerCase() === 'false') {
+ value = false
}
} else {
- value = nextArg;
+ value = nextArg
}
- } else if (typeof value === "boolean") {
- value = true;
+ } else if (typeof value === 'boolean') {
+ value = true
}
- cliLog("Returned value: ", value); // Debug log
- return value;
+ cliLog('Returned value: ', value) // Debug log
+ return value
}
export function parseArgument(
arg: string,
nextArg: string,
): { key: string | undefined; value: string | boolean | undefined } {
- let key: string | undefined = undefined;
- let value: string | boolean | undefined;
+ let key: string | undefined = undefined
+ let value: string | boolean | undefined
- if (arg.startsWith("--")) {
- key = arg.slice(2);
+ if (arg.startsWith('--')) {
+ key = arg.slice(2)
if (optionDefinitions.hasOwnProperty(key)) {
- const optionDef = optionDefinitions[key];
- value = getOptionValue(arg, nextArg, optionDef);
+ const optionDef = optionDefinitions[key]
+ value = getOptionValue(arg, nextArg, optionDef)
} else {
- value = true;
+ value = true
}
} else {
- throw new Error(`Invalid parameter: ${arg}`);
+ throw new Error(`Invalid parameter: ${arg}`)
}
- return { key, value };
+ return { key, value }
}
export async function parseCliArgs(): Promise {
try {
- const args = getArguments();
- const parsedArgs: ParsedArgs = {};
+ const args = getArguments()
+ const parsedArgs: ParsedArgs = {}
for (let i = 0; i < args.length; i++) {
- const { key, value } = parseArgument(args[i], args[i + 1]);
+ const { key, value } = parseArgument(args[i], args[i + 1])
if (key) {
- parsedArgs[key] = value;
+ parsedArgs[key] = value
}
}
- return parsedArgs;
+ return parsedArgs
} catch (error) {
- throw error;
+ throw error
}
}
@@ -102,40 +102,40 @@ export const getAdditionalPrompt = () =>
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
- });
- rl.question("Do you want to add anything else...", (additionalPrompt) => {
- rl.close();
- resolve(additionalPrompt);
- });
- });
+ })
+ rl.question('Do you want to add anything else...', (additionalPrompt) => {
+ rl.close()
+ resolve(additionalPrompt)
+ })
+ })
export const chooseActions = async (actionsConfig: Record): Promise> => {
- cliLog("\nChoose actions (separated by commas):");
- const actions = Object.keys(actionsConfig);
+ cliLog('\nChoose actions (separated by commas):')
+ const actions = Object.keys(actionsConfig)
actions.forEach((action, index) => {
- cliLog(`${index + 1}. ${action}`);
- });
+ cliLog(`${index + 1}. ${action}`)
+ })
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
- });
+ })
const actionIndexes = await new Promise((resolve) => {
- rl.question("Enter the numbers corresponding to the actions: ", (actionIndexes) => {
- rl.close();
- resolve(actionIndexes);
- });
- });
+ rl.question('Enter the numbers corresponding to the actions: ', (actionIndexes) => {
+ rl.close()
+ resolve(actionIndexes)
+ })
+ })
- const selectedIndexes = actionIndexes.split(",").map((index) => parseInt(index.trim()) - 1);
+ const selectedIndexes = actionIndexes.split(',').map((index) => parseInt(index.trim()) - 1)
- const validSelection = selectedIndexes.every((index) => index >= 0 && index < actions.length);
+ const validSelection = selectedIndexes.every((index) => index >= 0 && index < actions.length)
if (validSelection) {
- return selectedIndexes.map((index) => actions[index] as keyof typeof actionsConfig);
+ return selectedIndexes.map((index) => actions[index] as keyof typeof actionsConfig)
} else {
- cliLog("Invalid input, please try again.");
- return chooseActions(actionsConfig);
+ cliLog('Invalid input, please try again.')
+ return chooseActions(actionsConfig)
}
-};
+}
diff --git a/cli/create-cli-factory.test.ts b/cli/create-cli-factory.test.ts
index f458729..aa9ce05 100644
--- a/cli/create-cli-factory.test.ts
+++ b/cli/create-cli-factory.test.ts
@@ -1 +1 @@
-import { describe, expect, test } from "bun:test";
+import { describe, expect, test } from 'bun:test'
diff --git a/cli/create-cli-factory.ts b/cli/create-cli-factory.ts
index 050014f..ec2b42d 100644
--- a/cli/create-cli-factory.ts
+++ b/cli/create-cli-factory.ts
@@ -1,76 +1,70 @@
-import { fileFactory } from "../files-folders";
-import { defaultLogger } from "../logger";
-import { BaseError } from "../utils/base-error";
-import { chooseActions, getAdditionalPrompt, getUserInput, parseCliArgs } from "./cli-utils";
+import { fileFactory } from '../files-folders'
+import { defaultLogger } from '../logger'
+import { BaseError } from '../utils/base-error'
+import { chooseActions, getAdditionalPrompt, getUserInput, parseCliArgs } from './cli-utils'
export type CLIOptions = {
- inputPrompt?: string;
- actionsConfig?: Record;
- logger?: typeof defaultLogger;
+ inputPrompt?: string
+ actionsConfig?: Record
+ logger?: typeof defaultLogger
// fileConfig?: {
// filePath: string;
// fileContent: string;
// };
-};
+}
export function createCliFactory>({
- inputPrompt = "Please input your command",
+ inputPrompt = 'Please input your command',
actionsConfig = {},
logger,
}: CLIOptions) {
// const actionsConfig = options.actionsConfig ?? {};
const factory = fileFactory({
- baseDirectory: ".", // Replace with actual path
- });
+ baseDirectory: '.', // Replace with actual path
+ })
const processInput = async () => {
try {
- const commandLineArgs = await parseCliArgs();
+ const commandLineArgs = await parseCliArgs()
- const userInput = await getUserInput();
+ const userInput = await getUserInput()
// Handle user input and command line arguments...
- return { commandLineArgs, userInput };
+ return { commandLineArgs, userInput }
} catch (error) {
- throw error;
+ throw error
}
- };
+ }
const executeActions = async () => {
try {
- const additionalPrompt = await getAdditionalPrompt();
+ const additionalPrompt = await getAdditionalPrompt()
- const chosenActions = await chooseActions(actionsConfig);
+ const chosenActions = await chooseActions(actionsConfig)
// Execute chosen actions...
- return { additionalPrompt, chosenActions };
+ return { additionalPrompt, chosenActions }
} catch (error) {
- throw error;
+ throw error
}
- };
+ }
- const handleFiles = ({
- filePath,
- fileContent,
- }: {
- filePath: string;
- fileContent: string;
- }) => {
+ const handleFiles = ({ filePath, fileContent }: { filePath: string; fileContent: string }) => {
try {
- factory.directoryExists({ path: filePath });
+ factory.directoryExists({ path: filePath })
- factory.createFile(filePath, fileContent);
+ factory.createFile(filePath, fileContent)
} catch (error) {
- throw error;
+ throw error
}
- };
+ }
return {
inputPrompt,
processInput,
executeActions,
handleFiles,
- };
+ }
}
diff --git a/cli/index.ts b/cli/index.ts
index 92c9bc5..2758838 100644
--- a/cli/index.ts
+++ b/cli/index.ts
@@ -1 +1 @@
-export { createCliFactory } from "./create-cli-factory";
+export { createCliFactory } from './create-cli-factory'
diff --git a/client.tsx b/client.tsx
index f84240e..68664a9 100644
--- a/client.tsx
+++ b/client.tsx
@@ -1,10 +1,10 @@
-export { clientCookieFactory } from "./cookies/client-cookie-factory";
+export { clientCookieFactory } from './cookies/client-cookie-factory'
-export * as cookieUtils from "./cookies/cookie-utils";
+export * as cookieUtils from './cookies/cookie-utils'
-export type { CookieOptions } from "./cookies/cookie-types.ts";
+export type { CookieOptions } from './cookies/cookie-types.ts'
-export { createFetchFactory } from "./fetcher/create-fetch-factory.ts";
+export { createFetchFactory } from './fetcher/create-fetch-factory.ts'
export type {
APIConfig,
EventHandlerMap,
@@ -12,6 +12,6 @@ export type {
FileDownloadConfig,
MappedApiConfig,
TypeMap,
-} from "./fetcher/fetch-types.ts";
+} from './fetcher/fetch-types.ts'
-export {} from "./auth/security-token.ts";
+export {} from './auth/security-token.ts'
diff --git a/cookies/client-cookie-factory.test.ts b/cookies/client-cookie-factory.test.ts
index 938414e..f0d7651 100644
--- a/cookies/client-cookie-factory.test.ts
+++ b/cookies/client-cookie-factory.test.ts
@@ -1,68 +1,68 @@
-import { beforeEach, describe, expect, jest, test } from "bun:test";
-import { clientCookieFactory } from "./client-cookie-factory";
+import { beforeEach, describe, expect, jest, test } from 'bun:test'
+import { clientCookieFactory } from './client-cookie-factory'
declare var document: {
- cookie: any;
-};
+ cookie: any
+}
const mockDocument = {
- _cookie: "",
+ _cookie: '',
get cookie() {
- return this._cookie;
+ return this._cookie
},
set cookie(value) {
- this._cookie = value;
+ this._cookie = value
},
-};
+}
-Object.defineProperty(globalThis, "document", {
+Object.defineProperty(globalThis, 'document', {
value: mockDocument,
writable: true,
configurable: true,
-});
+})
-describe("createClientCookieFactory", () => {
- const cookieFactory = clientCookieFactory("test");
+describe('createClientCookieFactory', () => {
+ const cookieFactory = clientCookieFactory('test')
// Mock document.cookie
- let mockCookie = "";
+ let mockCookie = ''
beforeEach(() => {
// Reset the mock document.cookie before each test
- mockCookie = "";
- });
+ mockCookie = ''
+ })
- Object.defineProperty(document, "cookie", {
+ Object.defineProperty(document, 'cookie', {
get: jest.fn(() => mockCookie),
set: jest.fn((newCookie) => {
- mockCookie = newCookie;
+ mockCookie = newCookie
}),
- });
+ })
- test("setCookie sets a cookie", () => {
- cookieFactory.setCookie("value");
- expect(document.cookie).toBe("test=value");
- });
+ test('setCookie sets a cookie', () => {
+ cookieFactory.setCookie('value')
+ expect(document.cookie).toBe('test=value')
+ })
- test("getCookie returns the value of a cookie", () => {
- document.cookie = "test=value";
- const value = cookieFactory.getRawCookie();
- expect(value).toBe("value");
- });
+ test('getCookie returns the value of a cookie', () => {
+ document.cookie = 'test=value'
+ const value = cookieFactory.getRawCookie()
+ expect(value).toBe('value')
+ })
- test("deleteCookie sets a cookie with Max-Age=-1", () => {
- cookieFactory.deleteCookie();
- expect(document.cookie).toBe("test=; max-age=-1");
- });
+ test('deleteCookie sets a cookie with Max-Age=-1', () => {
+ cookieFactory.deleteCookie()
+ expect(document.cookie).toBe('test=; max-age=-1')
+ })
- test("checkCookie returns true if a cookie exists", () => {
- document.cookie = "test=value";
- const exists = cookieFactory.checkCookie();
- expect(exists).toBe(true);
- });
+ test('checkCookie returns true if a cookie exists', () => {
+ document.cookie = 'test=value'
+ const exists = cookieFactory.checkCookie()
+ expect(exists).toBe(true)
+ })
- test("checkCookie returns false if a cookie does not exist", () => {
- const exists = cookieFactory.checkCookie();
- expect(exists).toBe(false);
- });
-});
+ test('checkCookie returns false if a cookie does not exist', () => {
+ const exists = cookieFactory.checkCookie()
+ expect(exists).toBe(false)
+ })
+})
diff --git a/cookies/client-cookie-factory.ts b/cookies/client-cookie-factory.ts
index 4317aa3..29b6b2b 100644
--- a/cookies/client-cookie-factory.ts
+++ b/cookies/client-cookie-factory.ts
@@ -1,31 +1,31 @@
-import { CookieOptions } from "./cookie-types";
-import { parseCookieData, retrieveRawCookieValue, setCookie } from "./cookie-utils";
+import { CookieOptions } from './cookie-types'
+import { parseCookieData, retrieveRawCookieValue, setCookie } from './cookie-utils'
declare var document: {
- cookie: any;
-};
+ cookie: any
+}
export function clientCookieFactory(cookieKey: string, options?: CookieOptions) {
const handleSetCookie = (value: T, cookieSetOptions: CookieOptions = {}) => {
- setCookie(cookieKey, value, cookieSetOptions || options || {});
- };
+ setCookie(cookieKey, value, cookieSetOptions || options || {})
+ }
const getRawCookie = () => {
- return retrieveRawCookieValue(cookieKey);
- };
+ return retrieveRawCookieValue(cookieKey)
+ }
const deleteCookie = () => {
- handleSetCookie("" as T, { maxAge: -1 });
- };
+ handleSetCookie('' as T, { maxAge: -1 })
+ }
const checkCookie = () => {
- return getRawCookie() !== null;
- };
+ return getRawCookie() !== null
+ }
const getParsedCookie = (): T | null => {
- const rawCookie = getRawCookie();
- return parseCookieData(rawCookie);
- };
+ const rawCookie = getRawCookie()
+ return parseCookieData(rawCookie)
+ }
return {
setCookie: handleSetCookie,
@@ -33,5 +33,5 @@ export function clientCookieFactory(cookieKey: string, options?: Coo
checkCookie,
getParsedCookie,
getRawCookie,
- };
+ }
}
diff --git a/cookies/cookie-types.ts b/cookies/cookie-types.ts
index 3e345ee..15dc2eb 100644
--- a/cookies/cookie-types.ts
+++ b/cookies/cookie-types.ts
@@ -1,8 +1,8 @@
export type CookieOptions = {
- maxAge?: number;
- path?: string;
- domain?: string;
- secure?: boolean;
- httpOnly?: boolean;
- sameSite?: "Strict" | "Lax" | "None";
-};
+ maxAge?: number
+ path?: string
+ domain?: string
+ secure?: boolean
+ httpOnly?: boolean
+ sameSite?: 'Strict' | 'Lax' | 'None'
+}
diff --git a/cookies/cookie-utils.test.ts b/cookies/cookie-utils.test.ts
index 1b04052..4a1c8eb 100644
--- a/cookies/cookie-utils.test.ts
+++ b/cookies/cookie-utils.test.ts
@@ -1,66 +1,66 @@
-import { afterEach, describe, expect, it } from "bun:test";
-import { parseCookieData, retrieveRawCookieValue, stringifyCookieData } from "./cookie-utils";
+import { afterEach, describe, expect, it } from 'bun:test'
+import { parseCookieData, retrieveRawCookieValue, stringifyCookieData } from './cookie-utils'
declare var document: {
- cookie: any;
-};
+ cookie: any
+}
-describe("Cookie Helpers", () => {
- describe("parseCookieData", () => {
- it("should parse JSON string to object", () => {
- const jsonString = '{"key": "value"}';
- expect(parseCookieData(jsonString)).toEqual({ key: "value" });
- });
+describe('Cookie Helpers', () => {
+ describe('parseCookieData', () => {
+ it('should parse JSON string to object', () => {
+ const jsonString = '{"key": "value"}'
+ expect(parseCookieData(jsonString)).toEqual({ key: 'value' })
+ })
- it("should return string if parsing fails", () => {
- const invalidJson = "{key: 'value'}";
- expect(parseCookieData(invalidJson)).toBe(invalidJson);
- });
+ it('should return string if parsing fails', () => {
+ const invalidJson = "{key: 'value'}"
+ expect(parseCookieData(invalidJson)).toBe(invalidJson)
+ })
- it("should return null if input is null", () => {
- expect(parseCookieData(null)).toBeNull();
- });
- });
+ it('should return null if input is null', () => {
+ expect(parseCookieData(null)).toBeNull()
+ })
+ })
- describe("stringifyCookieData", () => {
- it("should stringify object to JSON string", () => {
- const obj = { key: "value" };
- expect(stringifyCookieData(obj)).toBe('{"key":"value"}');
- });
+ describe('stringifyCookieData', () => {
+ it('should stringify object to JSON string', () => {
+ const obj = { key: 'value' }
+ expect(stringifyCookieData(obj)).toBe('{"key":"value"}')
+ })
- it("should return string as is", () => {
- const str = "testString";
- expect(stringifyCookieData(str)).toBe(str);
- });
- });
-});
+ it('should return string as is', () => {
+ const str = 'testString'
+ expect(stringifyCookieData(str)).toBe(str)
+ })
+ })
+})
-describe("retrieveRawCookieValue", () => {
+describe('retrieveRawCookieValue', () => {
// Save original document.cookie
- const originalDocumentCookie = document.cookie;
+ const originalDocumentCookie = document.cookie
afterEach(() => {
// Restore original document.cookie after each test
- document.cookie = originalDocumentCookie;
- });
+ document.cookie = originalDocumentCookie
+ })
- it("should return the correct cookie value", () => {
- document.cookie = "testCookie=testValue; anotherCookie=anotherValue";
- expect(retrieveRawCookieValue("testCookie")).toBe("testValue");
- });
+ it('should return the correct cookie value', () => {
+ document.cookie = 'testCookie=testValue; anotherCookie=anotherValue'
+ expect(retrieveRawCookieValue('testCookie')).toBe('testValue')
+ })
- it("should return null if cookie is not found", () => {
- document.cookie = "testCookie=testValue; anotherCookie=anotherValue";
- expect(retrieveRawCookieValue("nonExistentCookie")).toBeNull();
- });
+ it('should return null if cookie is not found', () => {
+ document.cookie = 'testCookie=testValue; anotherCookie=anotherValue'
+ expect(retrieveRawCookieValue('nonExistentCookie')).toBeNull()
+ })
- it("should decode URI encoded cookie names and values", () => {
- document.cookie = "encodedName%3D=encodedValue%3D; anotherCookie=anotherValue";
- expect(retrieveRawCookieValue("encodedName=")).toBe("encodedValue=");
- });
+ it('should decode URI encoded cookie names and values', () => {
+ document.cookie = 'encodedName%3D=encodedValue%3D; anotherCookie=anotherValue'
+ expect(retrieveRawCookieValue('encodedName=')).toBe('encodedValue=')
+ })
- it("should handle cookies with no value", () => {
- document.cookie = "emptyCookie=; anotherCookie=anotherValue";
- expect(retrieveRawCookieValue("emptyCookie")).toBe("");
- });
-});
+ it('should handle cookies with no value', () => {
+ document.cookie = 'emptyCookie=; anotherCookie=anotherValue'
+ expect(retrieveRawCookieValue('emptyCookie')).toBe('')
+ })
+})
diff --git a/cookies/cookie-utils.ts b/cookies/cookie-utils.ts
index 9fb64bc..6636782 100644
--- a/cookies/cookie-utils.ts
+++ b/cookies/cookie-utils.ts
@@ -1,92 +1,92 @@
-import { CookieOptions } from "./cookie-types";
+import { CookieOptions } from './cookie-types'
declare var document: {
- cookie: any;
-};
+ cookie: any
+}
export const parseCookieData = (data: string | null): T | null => {
- if (data === null) return null;
- if (typeof data === "undefined") return null;
+ if (data === null) return null
+ if (typeof data === 'undefined') return null
try {
- return JSON.parse(data) as T;
+ return JSON.parse(data) as T
} catch (e) {
// If parsing fails, assume the data is a string
- return data as unknown as T;
+ return data as unknown as T
}
-};
+}
export const stringifyCookieData = (data: T): string => {
- if (typeof data === "string") {
- return data;
+ if (typeof data === 'string') {
+ return data
} else {
- return JSON.stringify(data);
+ return JSON.stringify(data)
}
-};
+}
export const retrieveRawCookieValue = (name: string): string | null => {
- const cookieArr = document.cookie.split("; ");
+ const cookieArr = document.cookie.split('; ')
for (let i = 0; i < cookieArr.length; i++) {
- const cookiePair = cookieArr[i].split("=");
+ const cookiePair = cookieArr[i].split('=')
if (name === decodeURIComponent(cookiePair[0])) {
- return decodeURIComponent(cookiePair[1]);
+ return decodeURIComponent(cookiePair[1])
}
}
- return null;
-};
+ return null
+}
export const encodeCookie = (cookieKey: string, value: T, options: CookieOptions): string => {
let cookieString = `${encodeURIComponent(cookieKey)}=${encodeURIComponent(
- typeof value === "string" ? value : JSON.stringify(value),
- )}`;
+ typeof value === 'string' ? value : JSON.stringify(value),
+ )}`
if (options.maxAge) {
- cookieString += `; max-age=${options.maxAge}`;
+ cookieString += `; max-age=${options.maxAge}`
}
if (options.path) {
- cookieString += `; path=${options.path}`;
+ cookieString += `; path=${options.path}`
}
if (options.domain) {
- cookieString += `; domain=${options.domain}`;
+ cookieString += `; domain=${options.domain}`
}
if (options.secure) {
- cookieString += `; secure`;
+ cookieString += `; secure`
}
if (options.httpOnly) {
- cookieString += `; httpOnly`;
+ cookieString += `; httpOnly`
}
- return cookieString;
-};
+ return cookieString
+}
export const setCookie = (cookieKey: string, value: T, options: CookieOptions) => {
- document.cookie = encodeCookie(cookieKey, value, options);
-};
+ document.cookie = encodeCookie(cookieKey, value, options)
+}
export function parseCookies(cookiesString: string) {
- const cookies: { [name: string]: string } = {};
- const pairs = cookiesString.split(";");
+ const cookies: { [name: string]: string } = {}
+ const pairs = cookiesString.split(';')
pairs.forEach((pair) => {
- const [name, ...rest] = pair.split("=");
- cookies[name.trim()] = rest.join("=").trim();
- });
+ const [name, ...rest] = pair.split('=')
+ cookies[name.trim()] = rest.join('=').trim()
+ })
- return cookies;
+ return cookies
}
export const getAllCookies = (req: Request): T => {
- const cookies = parseCookies(req?.headers.get("Cookie") || "");
- const parsedCookies: any = {};
+ const cookies = parseCookies(req?.headers.get('Cookie') || '')
+ const parsedCookies: any = {}
for (const [name, value] of Object.entries(cookies)) {
- parsedCookies[name] = parseCookieData(value) as any;
+ parsedCookies[name] = parseCookieData(value) as any
}
- return parsedCookies as T;
-};
+ return parsedCookies as T
+}
diff --git a/cookies/index.ts b/cookies/index.ts
index 22f5db7..fb33c97 100644
--- a/cookies/index.ts
+++ b/cookies/index.ts
@@ -1,5 +1,5 @@
-export { clientCookieFactory as createClientCookieFactory } from "./client-cookie-factory";
-export type { CookieOptions } from "./cookie-types";
+export { clientCookieFactory as createClientCookieFactory } from './client-cookie-factory'
+export type { CookieOptions } from './cookie-types'
export {
encodeCookie,
getAllCookies,
@@ -8,5 +8,5 @@ export {
retrieveRawCookieValue,
setCookie,
stringifyCookieData,
-} from "./cookie-utils";
-export { serverCookieFactory as createServerCookieFactory } from "./server-side-cookie-factory";
+} from './cookie-utils'
+export { serverCookieFactory as createServerCookieFactory } from './server-side-cookie-factory'
diff --git a/cookies/server-side-cookie-factory.test.ts b/cookies/server-side-cookie-factory.test.ts
index 4717d3c..b950ca8 100644
--- a/cookies/server-side-cookie-factory.test.ts
+++ b/cookies/server-side-cookie-factory.test.ts
@@ -1,53 +1,53 @@
-import { beforeEach, describe, expect, jest, test } from "bun:test";
-import { serverCookieFactory } from "./server-side-cookie-factory";
+import { beforeEach, describe, expect, jest, test } from 'bun:test'
+import { serverCookieFactory } from './server-side-cookie-factory'
-describe("createServerCookieFactory", () => {
- const cookieFactory = serverCookieFactory("test");
+describe('createServerCookieFactory', () => {
+ const cookieFactory = serverCookieFactory('test')
// Mock response and request objects
- let mockRes = { headers: { append: jest.fn() } };
- let mockReq = { headers: { get: jest.fn() } };
+ let mockRes = { headers: { append: jest.fn() } }
+ let mockReq = { headers: { get: jest.fn() } }
beforeEach(() => {
// Reset the mock functions before each test
- mockRes.headers.append.mockReset();
- mockReq.headers.get.mockReset();
- });
+ mockRes.headers.append.mockReset()
+ mockReq.headers.get.mockReset()
+ })
- test("setCookie appends a Set-Cookie header", () => {
- cookieFactory.setCookie("test", { res: mockRes as unknown as Response });
+ test('setCookie appends a Set-Cookie header', () => {
+ cookieFactory.setCookie('test', { res: mockRes as unknown as Response })
// bun doesn't currently support toHaveBeenCalledWith
// expect(mockRes.headers.append).toHaveBeenCalledWith(
// "Set-Cookie",
// "test=value"
// );
- expect(mockRes.headers.append).toHaveBeenCalled();
- });
+ expect(mockRes.headers.append).toHaveBeenCalled()
+ })
- test("getCookie returns the value of a cookie", () => {
- mockReq.headers.get.mockReturnValue("test=value");
- const value = cookieFactory.getCookie(false, mockReq as any as Request);
- expect(value).toBe("value");
- });
+ test('getCookie returns the value of a cookie', () => {
+ mockReq.headers.get.mockReturnValue('test=value')
+ const value = cookieFactory.getCookie(false, mockReq as any as Request)
+ expect(value).toBe('value')
+ })
- test("deleteCookie sets a cookie with Max-Age=-1", () => {
- cookieFactory.deleteCookie(mockRes as any as Response);
+ test('deleteCookie sets a cookie with Max-Age=-1', () => {
+ cookieFactory.deleteCookie(mockRes as any as Response)
// expect(mockRes.headers.append).toHaveBeenCalledWith(
// "Set-Cookie",
// "test=; Max-Age=-1"
// );
- expect(mockRes.headers.append).toHaveBeenCalled();
- });
-
- test("checkCookie returns true if a cookie exists", () => {
- mockReq.headers.get.mockReturnValue("test=value");
- const exists = cookieFactory.checkCookie(mockReq as unknown as Request);
- expect(exists).toBe(true);
- });
-
- test("checkCookie returns false if a cookie does not exist", () => {
- mockReq.headers.get.mockReturnValue("");
- const exists = cookieFactory.checkCookie(mockReq as unknown as Request);
- expect(exists).toBe(false);
- });
-});
+ expect(mockRes.headers.append).toHaveBeenCalled()
+ })
+
+ test('checkCookie returns true if a cookie exists', () => {
+ mockReq.headers.get.mockReturnValue('test=value')
+ const exists = cookieFactory.checkCookie(mockReq as unknown as Request)
+ expect(exists).toBe(true)
+ })
+
+ test('checkCookie returns false if a cookie does not exist', () => {
+ mockReq.headers.get.mockReturnValue('')
+ const exists = cookieFactory.checkCookie(mockReq as unknown as Request)
+ expect(exists).toBe(false)
+ })
+})
diff --git a/cookies/server-side-cookie-factory.ts b/cookies/server-side-cookie-factory.ts
index 8561fad..86f6574 100644
--- a/cookies/server-side-cookie-factory.ts
+++ b/cookies/server-side-cookie-factory.ts
@@ -1,5 +1,5 @@
-import { CookieOptions } from "./cookie-types";
-import { encodeCookie, parseCookieData, parseCookies, stringifyCookieData } from "./cookie-utils";
+import { CookieOptions } from './cookie-types'
+import { encodeCookie, parseCookieData, parseCookies, stringifyCookieData } from './cookie-utils'
export function serverCookieFactory<
T = string,
@@ -12,9 +12,9 @@ export function serverCookieFactory<
request,
response,
}: {
- request?: FactoryRequest;
- response?: FactoryRes;
- options?: CookieOptions;
+ request?: FactoryRequest
+ response?: FactoryRes
+ options?: CookieOptions
} = {},
) {
const setCookie = (
@@ -23,52 +23,52 @@ export function serverCookieFactory<
options = optionsCfg || {},
res = response,
}: {
- options?: CookieOptions;
- res?: Response | undefined;
+ options?: CookieOptions
+ res?: Response | undefined
} = {},
) => {
- let cookieValue = typeof value === "string" ? value : stringifyCookieData(value);
+ let cookieValue = typeof value === 'string' ? value : stringifyCookieData(value)
- const cookieString = encodeCookie(cookieKey, cookieValue, options);
+ const cookieString = encodeCookie(cookieKey, cookieValue, options)
if (!res) {
- throw new Error("No response object provided");
+ throw new Error('No response object provided')
}
- res?.headers.append("Set-Cookie", cookieString);
- };
+ res?.headers.append('Set-Cookie', cookieString)
+ }
const deleteCookie = (res = response) => {
if (!res) {
- throw new Error("No response object provided");
+ throw new Error('No response object provided')
}
- setCookie("" as unknown as T, {
+ setCookie('' as unknown as T, {
options: { maxAge: -1 },
res,
- });
- };
+ })
+ }
const getCookie = (uriDecode = false, req = request): T | null => {
if (!req) {
- throw new Error("No request object provided");
+ throw new Error('No request object provided')
}
- const cookies = parseCookies(req.headers.get("Cookie") || "");
- const cookie = cookies[cookieKey];
- return parseCookieData(uriDecode ? decodeURIComponent(cookie) : cookie);
- };
+ const cookies = parseCookies(req.headers.get('Cookie') || '')
+ const cookie = cookies[cookieKey]
+ return parseCookieData(uriDecode ? decodeURIComponent(cookie) : cookie)
+ }
const checkCookie = (req = request) => {
- return getCookie(false, req) !== null;
- };
+ return getCookie(false, req) !== null
+ }
const getRawCookie = (req = request): string | null => {
if (!req) {
- throw new Error("No request object provided");
+ throw new Error('No request object provided')
}
- const cookies = parseCookies(req.headers.get("Cookie") || "");
- return cookies[cookieKey] || null;
- };
+ const cookies = parseCookies(req.headers.get('Cookie') || '')
+ return cookies[cookieKey] || null
+ }
return {
setCookie,
@@ -76,5 +76,5 @@ export function serverCookieFactory<
deleteCookie,
checkCookie,
getRawCookie,
- };
+ }
}
diff --git a/data-gen/cities.ts b/data-gen/cities.ts
index a445aab..a45768e 100644
--- a/data-gen/cities.ts
+++ b/data-gen/cities.ts
@@ -1,112 +1,112 @@
-import { randFromArray } from "./rand-num";
+import { randFromArray } from './rand-num'
const cities = [
- "Albany",
- "Austin",
- "Baltimore",
- "Boston",
- "Charlotte",
- "Chicago",
- "Dallas",
- "Denver",
- "Detroit",
- "Houston",
- "Indianapolis",
- "Jacksonville",
- "Kansas City",
- "Las Vegas",
- "Los Angeles",
- "Louisville",
- "Memphis",
- "Miami",
- "Minneapolis",
- "Nashville",
- "New Orleans",
- "New York",
- "Oklahoma City",
- "Orlando",
- "Philadelphia",
- "Phoenix",
- "Portland",
- "Raleigh",
- "Sacramento",
- "San Antonio",
- "San Diego",
- "San Francisco",
- "San Jose",
- "Seattle",
- "St. Louis",
- "Tampa",
- "Washington, D.C.",
- "Albuquerque",
- "Atlanta",
- "Boise",
- "Buffalo",
- "Cincinnati",
- "Cleveland",
- "Columbus",
- "Des Moines",
- "El Paso",
- "Fort Worth",
- "Fresno",
- "Honolulu",
- "Indianapolis",
- "Jacksonville",
- "Kansas City",
- "Las Vegas",
- "Little Rock",
- "Madison",
- "Manchester",
- "Milwaukee",
- "Mobile",
- "Montgomery",
- "Newark",
- "Oakland",
- "Omaha",
- "Pittsburgh",
- "Reno",
- "Richmond",
- "Riverside",
- "Salt Lake City",
- "Santa Ana",
- "Savannah",
- "Syracuse",
- "Tacoma",
- "Toledo",
- "Tucson",
- "Tulsa",
- "Virginia Beach",
- "Wichita",
- "Wilmington",
- "Anchorage",
- "Arlington",
- "Aurora",
- "Bakersfield",
- "Chandler",
- "Chesapeake",
- "Chula Vista",
- "Durham",
- "Fremont",
- "Garland",
- "Gilbert",
- "Glendale",
- "Hialeah",
- "Irvine",
- "Irving",
- "Laredo",
- "Mesa",
- "Norfolk",
- "North Las Vegas",
- "Plano",
- "Raleigh",
- "Riverside",
- "Rochester",
- "Scottsdale",
- "Spokane",
- "Stockton",
- "Toledo",
-];
+ 'Albany',
+ 'Austin',
+ 'Baltimore',
+ 'Boston',
+ 'Charlotte',
+ 'Chicago',
+ 'Dallas',
+ 'Denver',
+ 'Detroit',
+ 'Houston',
+ 'Indianapolis',
+ 'Jacksonville',
+ 'Kansas City',
+ 'Las Vegas',
+ 'Los Angeles',
+ 'Louisville',
+ 'Memphis',
+ 'Miami',
+ 'Minneapolis',
+ 'Nashville',
+ 'New Orleans',
+ 'New York',
+ 'Oklahoma City',
+ 'Orlando',
+ 'Philadelphia',
+ 'Phoenix',
+ 'Portland',
+ 'Raleigh',
+ 'Sacramento',
+ 'San Antonio',
+ 'San Diego',
+ 'San Francisco',
+ 'San Jose',
+ 'Seattle',
+ 'St. Louis',
+ 'Tampa',
+ 'Washington, D.C.',
+ 'Albuquerque',
+ 'Atlanta',
+ 'Boise',
+ 'Buffalo',
+ 'Cincinnati',
+ 'Cleveland',
+ 'Columbus',
+ 'Des Moines',
+ 'El Paso',
+ 'Fort Worth',
+ 'Fresno',
+ 'Honolulu',
+ 'Indianapolis',
+ 'Jacksonville',
+ 'Kansas City',
+ 'Las Vegas',
+ 'Little Rock',
+ 'Madison',
+ 'Manchester',
+ 'Milwaukee',
+ 'Mobile',
+ 'Montgomery',
+ 'Newark',
+ 'Oakland',
+ 'Omaha',
+ 'Pittsburgh',
+ 'Reno',
+ 'Richmond',
+ 'Riverside',
+ 'Salt Lake City',
+ 'Santa Ana',
+ 'Savannah',
+ 'Syracuse',
+ 'Tacoma',
+ 'Toledo',
+ 'Tucson',
+ 'Tulsa',
+ 'Virginia Beach',
+ 'Wichita',
+ 'Wilmington',
+ 'Anchorage',
+ 'Arlington',
+ 'Aurora',
+ 'Bakersfield',
+ 'Chandler',
+ 'Chesapeake',
+ 'Chula Vista',
+ 'Durham',
+ 'Fremont',
+ 'Garland',
+ 'Gilbert',
+ 'Glendale',
+ 'Hialeah',
+ 'Irvine',
+ 'Irving',
+ 'Laredo',
+ 'Mesa',
+ 'Norfolk',
+ 'North Las Vegas',
+ 'Plano',
+ 'Raleigh',
+ 'Riverside',
+ 'Rochester',
+ 'Scottsdale',
+ 'Spokane',
+ 'Stockton',
+ 'Toledo',
+]
export const getRandomCity = () => {
- return randFromArray(cities);
-};
+ return randFromArray(cities)
+}
diff --git a/data-gen/countries.ts b/data-gen/countries.ts
index 4b537b5..2c25bf2 100644
--- a/data-gen/countries.ts
+++ b/data-gen/countries.ts
@@ -1,204 +1,204 @@
-import { randFromArray } from "./rand-num";
+import { randFromArray } from './rand-num'
const countries = [
- "Afghanistan",
- "Albania",
- "Algeria",
- "Andorra",
- "Angola",
- "Antigua and Barbuda",
- "Argentina",
- "Armenia",
- "Australia",
- "Austria",
- "Azerbaijan",
- "Bahamas",
- "Bahrain",
- "Bangladesh",
- "Barbados",
- "Belarus",
- "Belgium",
- "Belize",
- "Benin",
- "Bhutan",
- "Bolivia",
- "Bosnia and Herzegovina",
- "Botswana",
- "Brazil",
- "Brunei",
- "Bulgaria",
- "Burkina Faso",
- "Burundi",
- "Cabo Verde",
- "Cambodia",
- "Cameroon",
- "Canada",
- "Central African Republic",
- "Chad",
- "Chile",
- "China",
- "Colombia",
- "Comoros",
- "Congo",
- "Costa Rica",
- "Croatia",
- "Cuba",
- "Cyprus",
- "Czech Republic",
- "Democratic Republic of the Congo",
- "Denmark",
- "Djibouti",
- "Dominica",
- "Dominican Republic",
- "East Timor",
- "Ecuador",
- "Egypt",
- "El Salvador",
- "Equatorial Guinea",
- "Eritrea",
- "Estonia",
- "Eswatini",
- "Ethiopia",
- "Fiji",
- "Finland",
- "France",
- "Gabon",
- "Gambia",
- "Georgia",
- "Germany",
- "Ghana",
- "Greece",
- "Grenada",
- "Guatemala",
- "Guinea",
- "Guinea-Bissau",
- "Guyana",
- "Haiti",
- "Honduras",
- "Hungary",
- "Iceland",
- "India",
- "Indonesia",
- "Iran",
- "Iraq",
- "Ireland",
- "Israel",
- "Italy",
- "Ivory Coast",
- "Jamaica",
- "Japan",
- "Jordan",
- "Kazakhstan",
- "Kenya",
- "Kiribati",
- "Kosovo",
- "Kuwait",
- "Kyrgyzstan",
- "Laos",
- "Latvia",
- "Lebanon",
- "Lesotho",
- "Liberia",
- "Libya",
- "Liechtenstein",
- "Lithuania",
- "Luxembourg",
- "Madagascar",
- "Malawi",
- "Malaysia",
- "Maldives",
- "Mali",
- "Malta",
- "Marshall Islands",
- "Mauritania",
- "Mauritius",
- "Mexico",
- "Micronesia",
- "Moldova",
- "Monaco",
- "Mongolia",
- "Montenegro",
- "Morocco",
- "Mozambique",
- "Myanmar",
- "Namibia",
- "Nauru",
- "Nepal",
- "Netherlands",
- "New Zealand",
- "Nicaragua",
- "Niger",
- "Nigeria",
- "North Macedonia",
- "Norway",
- "Oman",
- "Pakistan",
- "Palau",
- "Palestine",
- "Panama",
- "Papua New Guinea",
- "Paraguay",
- "Peru",
- "Philippines",
- "Poland",
- "Portugal",
- "Qatar",
- "Romania",
- "Russia",
- "Rwanda",
- "Saint Kitts and Nevis",
- "Saint Lucia",
- "Saint Vincent and the Grenadines",
- "Samoa",
- "San Marino",
- "Sao Tome and Principe",
- "Saudi Arabia",
- "Senegal",
- "Serbia",
- "Seychelles",
- "Sierra Leone",
- "Singapore",
- "Slovakia",
- "Slovenia",
- "Solomon Islands",
- "Somalia",
- "South Africa",
- "South Korea",
- "South Sudan",
- "Spain",
- "Sri Lanka",
- "Sudan",
- "Suriname",
- "Sweden",
- "Switzerland",
- "Syria",
- "Taiwan",
- "Tajikistan",
- "Tanzania",
- "Thailand",
- "Togo",
- "Tonga",
- "Trinidad and Tobago",
- "Tunisia",
- "Turkey",
- "Turkmenistan",
- "Tuvalu",
- "Uganda",
- "Ukraine",
- "United Arab Emirates",
- "United Kingdom",
- "United States of America",
- "Uruguay",
- "Uzbekistan",
- "Vanuatu",
- "Vatican City",
- "Venezuela",
- "Vietnam",
- "Yemen",
- "Zambia",
- "Zimbabwe",
-];
+ 'Afghanistan',
+ 'Albania',
+ 'Algeria',
+ 'Andorra',
+ 'Angola',
+ 'Antigua and Barbuda',
+ 'Argentina',
+ 'Armenia',
+ 'Australia',
+ 'Austria',
+ 'Azerbaijan',
+ 'Bahamas',
+ 'Bahrain',
+ 'Bangladesh',
+ 'Barbados',
+ 'Belarus',
+ 'Belgium',
+ 'Belize',
+ 'Benin',
+ 'Bhutan',
+ 'Bolivia',
+ 'Bosnia and Herzegovina',
+ 'Botswana',
+ 'Brazil',
+ 'Brunei',
+ 'Bulgaria',
+ 'Burkina Faso',
+ 'Burundi',
+ 'Cabo Verde',
+ 'Cambodia',
+ 'Cameroon',
+ 'Canada',
+ 'Central African Republic',
+ 'Chad',
+ 'Chile',
+ 'China',
+ 'Colombia',
+ 'Comoros',
+ 'Congo',
+ 'Costa Rica',
+ 'Croatia',
+ 'Cuba',
+ 'Cyprus',
+ 'Czech Republic',
+ 'Democratic Republic of the Congo',
+ 'Denmark',
+ 'Djibouti',
+ 'Dominica',
+ 'Dominican Republic',
+ 'East Timor',
+ 'Ecuador',
+ 'Egypt',
+ 'El Salvador',
+ 'Equatorial Guinea',
+ 'Eritrea',
+ 'Estonia',
+ 'Eswatini',
+ 'Ethiopia',
+ 'Fiji',
+ 'Finland',
+ 'France',
+ 'Gabon',
+ 'Gambia',
+ 'Georgia',
+ 'Germany',
+ 'Ghana',
+ 'Greece',
+ 'Grenada',
+ 'Guatemala',
+ 'Guinea',
+ 'Guinea-Bissau',
+ 'Guyana',
+ 'Haiti',
+ 'Honduras',
+ 'Hungary',
+ 'Iceland',
+ 'India',
+ 'Indonesia',
+ 'Iran',
+ 'Iraq',
+ 'Ireland',
+ 'Israel',
+ 'Italy',
+ 'Ivory Coast',
+ 'Jamaica',
+ 'Japan',
+ 'Jordan',
+ 'Kazakhstan',
+ 'Kenya',
+ 'Kiribati',
+ 'Kosovo',
+ 'Kuwait',
+ 'Kyrgyzstan',
+ 'Laos',
+ 'Latvia',
+ 'Lebanon',
+ 'Lesotho',
+ 'Liberia',
+ 'Libya',
+ 'Liechtenstein',
+ 'Lithuania',
+ 'Luxembourg',
+ 'Madagascar',
+ 'Malawi',
+ 'Malaysia',
+ 'Maldives',
+ 'Mali',
+ 'Malta',
+ 'Marshall Islands',
+ 'Mauritania',
+ 'Mauritius',
+ 'Mexico',
+ 'Micronesia',
+ 'Moldova',
+ 'Monaco',
+ 'Mongolia',
+ 'Montenegro',
+ 'Morocco',
+ 'Mozambique',
+ 'Myanmar',
+ 'Namibia',
+ 'Nauru',
+ 'Nepal',
+ 'Netherlands',
+ 'New Zealand',
+ 'Nicaragua',
+ 'Niger',
+ 'Nigeria',
+ 'North Macedonia',
+ 'Norway',
+ 'Oman',
+ 'Pakistan',
+ 'Palau',
+ 'Palestine',
+ 'Panama',
+ 'Papua New Guinea',
+ 'Paraguay',
+ 'Peru',
+ 'Philippines',
+ 'Poland',
+ 'Portugal',
+ 'Qatar',
+ 'Romania',
+ 'Russia',
+ 'Rwanda',
+ 'Saint Kitts and Nevis',
+ 'Saint Lucia',
+ 'Saint Vincent and the Grenadines',
+ 'Samoa',
+ 'San Marino',
+ 'Sao Tome and Principe',
+ 'Saudi Arabia',
+ 'Senegal',
+ 'Serbia',
+ 'Seychelles',
+ 'Sierra Leone',
+ 'Singapore',
+ 'Slovakia',
+ 'Slovenia',
+ 'Solomon Islands',
+ 'Somalia',
+ 'South Africa',
+ 'South Korea',
+ 'South Sudan',
+ 'Spain',
+ 'Sri Lanka',
+ 'Sudan',
+ 'Suriname',
+ 'Sweden',
+ 'Switzerland',
+ 'Syria',
+ 'Taiwan',
+ 'Tajikistan',
+ 'Tanzania',
+ 'Thailand',
+ 'Togo',
+ 'Tonga',
+ 'Trinidad and Tobago',
+ 'Tunisia',
+ 'Turkey',
+ 'Turkmenistan',
+ 'Tuvalu',
+ 'Uganda',
+ 'Ukraine',
+ 'United Arab Emirates',
+ 'United Kingdom',
+ 'United States of America',
+ 'Uruguay',
+ 'Uzbekistan',
+ 'Vanuatu',
+ 'Vatican City',
+ 'Venezuela',
+ 'Vietnam',
+ 'Yemen',
+ 'Zambia',
+ 'Zimbabwe',
+]
export const getRandomCountry = () => {
- return randFromArray(countries);
-};
+ return randFromArray(countries)
+}
diff --git a/data-gen/create-random-data.test.ts b/data-gen/create-random-data.test.ts
index b982360..d0f4227 100644
--- a/data-gen/create-random-data.test.ts
+++ b/data-gen/create-random-data.test.ts
@@ -1,36 +1,36 @@
-import { describe, expect, it } from "bun:test";
-import { dataGenerators } from "./object-gen";
-import { createRandomData } from "./create-random-data";
+import { describe, expect, it } from 'bun:test'
+import { dataGenerators } from './object-gen'
+import { createRandomData } from './create-random-data'
-describe("createRandomData", () => {
- it("should return an object with the correct keys", () => {
+describe('createRandomData', () => {
+ it('should return an object with the correct keys', () => {
const config = {
- firstName: { type: "firstName" },
- lastName: { type: "lastName" },
- age: { type: "num", min: 18, max: 65 },
- } as const;
- const result = createRandomData(config);
- expect(result).toHaveProperty("firstName");
- expect(result).toHaveProperty("lastName");
- expect(result).toHaveProperty("age");
- });
+ firstName: { type: 'firstName' },
+ lastName: { type: 'lastName' },
+ age: { type: 'num', min: 18, max: 65 },
+ } as const
+ const result = createRandomData(config)
+ expect(result).toHaveProperty('firstName')
+ expect(result).toHaveProperty('lastName')
+ expect(result).toHaveProperty('age')
+ })
- it("should return a number within the specified range for num type config", () => {
+ it('should return a number within the specified range for num type config', () => {
const config = {
- age: { type: "num", min: 18, max: 65 },
- } as const;
- const result = createRandomData(config);
- expect(result.age).toBeGreaterThanOrEqual(18);
- expect(result.age).toBeLessThanOrEqual(65);
- });
+ age: { type: 'num', min: 18, max: 65 },
+ } as const
+ const result = createRandomData(config)
+ expect(result.age).toBeGreaterThanOrEqual(18)
+ expect(result.age).toBeLessThanOrEqual(65)
+ })
- it("should return a string for non-num type config", () => {
+ it('should return a string for non-num type config', () => {
const config = {
- firstName: { type: "firstName" },
- lastName: { type: "lastName" },
- } as const;
- const result = createRandomData(config);
- expect(typeof result.firstName).toBe("string");
- expect(typeof result.lastName).toBe("string");
- });
-});
+ firstName: { type: 'firstName' },
+ lastName: { type: 'lastName' },
+ } as const
+ const result = createRandomData(config)
+ expect(typeof result.firstName).toBe('string')
+ expect(typeof result.lastName).toBe('string')
+ })
+})
diff --git a/data-gen/create-random-data.ts b/data-gen/create-random-data.ts
index b1f3af6..9c067af 100644
--- a/data-gen/create-random-data.ts
+++ b/data-gen/create-random-data.ts
@@ -1,22 +1,22 @@
-import { getRandomCity } from "./cities";
-import { getRandomCountry } from "./countries";
-import { getRandomFirstName, getRandomFullName, getRandomLastName } from "./names";
-import { randNum } from "./rand-num";
-import { getRandState } from "./states";
+import { getRandomCity } from './cities'
+import { getRandomCountry } from './countries'
+import { getRandomFirstName, getRandomFullName, getRandomLastName } from './names'
+import { randNum } from './rand-num'
+import { getRandState } from './states'
type NumConfig = {
- type: "num";
- min?: number;
- max?: number;
-};
+ type: 'num'
+ min?: number
+ max?: number
+}
type OtherConfig = {
- type: Exclude;
-};
+ type: Exclude
+}
-type OutputT = T extends { type: "num" } ? number : ReturnType;
+type OutputT = T extends { type: 'num' } ? number : ReturnType
-type DataConfigItem = NumConfig | OtherConfig;
+type DataConfigItem = NumConfig | OtherConfig
export const dataGeneratorMap = {
city: getRandomCity,
@@ -26,39 +26,41 @@ export const dataGeneratorMap = {
country: getRandomCountry,
state: getRandState,
num: randNum,
-};
+}
export type DataGeneratorMapConfig = {
- city: {};
- firstName: {};
- lastName: {};
- fullName: {};
- country: {};
- state: {};
- num: { min: number; max: number };
-};
+ city: {}
+ firstName: {}
+ lastName: {}
+ fullName: {}
+ country: {}
+ state: {}
+ num: { min: number; max: number }
+}
-type DataGenMap = typeof dataGeneratorMap;
+type DataGenMap = typeof dataGeneratorMap
-export function createRandomData>(config: T): {
- [K in keyof T]: OutputT;
+export function createRandomData>(
+ config: T,
+): {
+ [K in keyof T]: OutputT
} {
- const result: any = {};
+ const result: any = {}
for (const key in config) {
- const itemConfig = config[key];
- if (itemConfig.type === "num") {
- const { min = 0, max = 100 } = itemConfig;
- result[key] = dataGeneratorMap[itemConfig.type](min, max);
+ const itemConfig = config[key]
+ if (itemConfig.type === 'num') {
+ const { min = 0, max = 100 } = itemConfig
+ result[key] = dataGeneratorMap[itemConfig.type](min, max)
} else {
- result[key] = dataGeneratorMap[itemConfig.type]();
+ result[key] = dataGeneratorMap[itemConfig.type]()
}
}
- return result;
+ return result
}
const dataGen = {
- first: { type: "firstName" }, // Explicitly type this as OtherConfig
- age: { type: "num", min: 18, max: 65 }, // Explicitly type this as NumConfig
-} as const;
+ first: { type: 'firstName' }, // Explicitly type this as OtherConfig
+ age: { type: 'num', min: 18, max: 65 }, // Explicitly type this as NumConfig
+} as const
diff --git a/data-gen/index.ts b/data-gen/index.ts
index 5772eb5..e97836a 100644
--- a/data-gen/index.ts
+++ b/data-gen/index.ts
@@ -1,13 +1,9 @@
-export { getRandomCity } from "./cities";
-export { getRandomCountry } from "./countries";
-export { createRandomData, dataGeneratorMap } from "./create-random-data";
-export type { DataGeneratorMapConfig } from "./create-random-data";
-export {
- getRandomFirstName,
- getRandomFullName,
- getRandomLastName,
-} from "./names";
-export { dataGenerators, inferTypeAndGenerate } from "./object-gen";
-export { randDateRange } from "./rand-date-range";
-export { randFromArray, randNum } from "./rand-num";
-export { getRandState } from "./states";
+export { getRandomCity } from './cities'
+export { getRandomCountry } from './countries'
+export { createRandomData, dataGeneratorMap } from './create-random-data'
+export type { DataGeneratorMapConfig } from './create-random-data'
+export { getRandomFirstName, getRandomFullName, getRandomLastName } from './names'
+export { dataGenerators, inferTypeAndGenerate } from './object-gen'
+export { randDateRange } from './rand-date-range'
+export { randFromArray, randNum } from './rand-num'
+export { getRandState } from './states'
diff --git a/data-gen/names.ts b/data-gen/names.ts
index 3217199..e9a9d07 100644
--- a/data-gen/names.ts
+++ b/data-gen/names.ts
@@ -1,198 +1,198 @@
-import { randFromArray } from "./rand-num";
+import { randFromArray } from './rand-num'
export const firstNames = [
- "Adam",
- "Alex",
- "Alice",
- "Andrew",
- "Anna",
- "Anthony",
- "Barbara",
- "Betty",
- "Brandon",
- "Brian",
- "Carol",
- "Charles",
- "Christine",
- "Christopher",
- "Cynthia",
- "Daniel",
- "David",
- "Deborah",
- "Donald",
- "Donna",
- "Dorothy",
- "Edward",
- "Elizabeth",
- "Emily",
- "Eric",
- "Evelyn",
- "Frank",
- "George",
- "Grace",
- "Helen",
- "Henry",
- "Jack",
- "James",
- "Jane",
- "Jason",
- "Jennifer",
- "Jessica",
- "John",
- "Joseph",
- "Joshua",
- "Julie",
- "Karen",
- "Katherine",
- "Kathleen",
- "Kenneth",
- "Kevin",
- "Laura",
- "Linda",
- "Lisa",
- "Margaret",
- "Maria",
- "Mark",
- "Mary",
- "Matthew",
- "Melissa",
- "Michael",
- "Michelle",
- "Nancy",
- "Nicole",
- "Patricia",
- "Patrick",
- "Paul",
- "Peter",
- "Rachel",
- "Raymond",
- "Richard",
- "Robert",
- "Roger",
- "Ronald",
- "Ryan",
- "Sandra",
- "Sarah",
- "Scott",
- "Sharon",
- "Stephen",
- "Susan",
- "Teresa",
- "Thomas",
- "Timothy",
- "Virginia",
- "Walter",
- "William",
-];
+ 'Adam',
+ 'Alex',
+ 'Alice',
+ 'Andrew',
+ 'Anna',
+ 'Anthony',
+ 'Barbara',
+ 'Betty',
+ 'Brandon',
+ 'Brian',
+ 'Carol',
+ 'Charles',
+ 'Christine',
+ 'Christopher',
+ 'Cynthia',
+ 'Daniel',
+ 'David',
+ 'Deborah',
+ 'Donald',
+ 'Donna',
+ 'Dorothy',
+ 'Edward',
+ 'Elizabeth',
+ 'Emily',
+ 'Eric',
+ 'Evelyn',
+ 'Frank',
+ 'George',
+ 'Grace',
+ 'Helen',
+ 'Henry',
+ 'Jack',
+ 'James',
+ 'Jane',
+ 'Jason',
+ 'Jennifer',
+ 'Jessica',
+ 'John',
+ 'Joseph',
+ 'Joshua',
+ 'Julie',
+ 'Karen',
+ 'Katherine',
+ 'Kathleen',
+ 'Kenneth',
+ 'Kevin',
+ 'Laura',
+ 'Linda',
+ 'Lisa',
+ 'Margaret',
+ 'Maria',
+ 'Mark',
+ 'Mary',
+ 'Matthew',
+ 'Melissa',
+ 'Michael',
+ 'Michelle',
+ 'Nancy',
+ 'Nicole',
+ 'Patricia',
+ 'Patrick',
+ 'Paul',
+ 'Peter',
+ 'Rachel',
+ 'Raymond',
+ 'Richard',
+ 'Robert',
+ 'Roger',
+ 'Ronald',
+ 'Ryan',
+ 'Sandra',
+ 'Sarah',
+ 'Scott',
+ 'Sharon',
+ 'Stephen',
+ 'Susan',
+ 'Teresa',
+ 'Thomas',
+ 'Timothy',
+ 'Virginia',
+ 'Walter',
+ 'William',
+]
export const lastNames = [
- "Adams",
- "Allen",
- "Anderson",
- "Bailey",
- "Baker",
- "Barnes",
- "Bell",
- "Bennett",
- "Brooks",
- "Brown",
- "Bryant",
- "Campbell",
- "Carter",
- "Clark",
- "Collins",
- "Cook",
- "Cooper",
- "Cox",
- "Davis",
- "Diaz",
- "Edwards",
- "Evans",
- "Foster",
- "Flores",
- "Garcia",
- "Gonzales",
- "Gonzalez",
- "Gray",
- "Green",
- "Griffin",
- "Hall",
- "Harris",
- "Hayes",
- "Henderson",
- "Hernandez",
- "Hill",
- "Howard",
- "Hughes",
- "Jackson",
- "James",
- "Jenkins",
- "Johnson",
- "Jones",
- "Kelly",
- "King",
- "Lee",
- "Lewis",
- "Lopez",
- "Long",
- "Martinez",
- "Martin",
- "Miller",
- "Mitchell",
- "Moore",
- "Morgan",
- "Morris",
- "Murphy",
- "Nelson",
- "Parker",
- "Patterson",
- "Perez",
- "Perry",
- "Peterson",
- "Phillips",
- "Powell",
- "Price",
- "Ramirez",
- "Reed",
- "Richardson",
- "Rivera",
- "Roberts",
- "Robinson",
- "Rodriguez",
- "Rogers",
- "Ross",
- "Russell",
- "Sanchez",
- "Sanders",
- "Scott",
- "Simmons",
- "Smith",
- "Stewart",
- "Taylor",
- "Thomas",
- "Thompson",
- "Torres",
- "Turner",
- "Walker",
- "Washington",
- "Watson",
- "Ward",
- "White",
- "Williams",
- "Wilson",
- "Wood",
- "Wright",
- "Young",
-];
+ 'Adams',
+ 'Allen',
+ 'Anderson',
+ 'Bailey',
+ 'Baker',
+ 'Barnes',
+ 'Bell',
+ 'Bennett',
+ 'Brooks',
+ 'Brown',
+ 'Bryant',
+ 'Campbell',
+ 'Carter',
+ 'Clark',
+ 'Collins',
+ 'Cook',
+ 'Cooper',
+ 'Cox',
+ 'Davis',
+ 'Diaz',
+ 'Edwards',
+ 'Evans',
+ 'Foster',
+ 'Flores',
+ 'Garcia',
+ 'Gonzales',
+ 'Gonzalez',
+ 'Gray',
+ 'Green',
+ 'Griffin',
+ 'Hall',
+ 'Harris',
+ 'Hayes',
+ 'Henderson',
+ 'Hernandez',
+ 'Hill',
+ 'Howard',
+ 'Hughes',
+ 'Jackson',
+ 'James',
+ 'Jenkins',
+ 'Johnson',
+ 'Jones',
+ 'Kelly',
+ 'King',
+ 'Lee',
+ 'Lewis',
+ 'Lopez',
+ 'Long',
+ 'Martinez',
+ 'Martin',
+ 'Miller',
+ 'Mitchell',
+ 'Moore',
+ 'Morgan',
+ 'Morris',
+ 'Murphy',
+ 'Nelson',
+ 'Parker',
+ 'Patterson',
+ 'Perez',
+ 'Perry',
+ 'Peterson',
+ 'Phillips',
+ 'Powell',
+ 'Price',
+ 'Ramirez',
+ 'Reed',
+ 'Richardson',
+ 'Rivera',
+ 'Roberts',
+ 'Robinson',
+ 'Rodriguez',
+ 'Rogers',
+ 'Ross',
+ 'Russell',
+ 'Sanchez',
+ 'Sanders',
+ 'Scott',
+ 'Simmons',
+ 'Smith',
+ 'Stewart',
+ 'Taylor',
+ 'Thomas',
+ 'Thompson',
+ 'Torres',
+ 'Turner',
+ 'Walker',
+ 'Washington',
+ 'Watson',
+ 'Ward',
+ 'White',
+ 'Williams',
+ 'Wilson',
+ 'Wood',
+ 'Wright',
+ 'Young',
+]
export const getRandomFirstName = () => {
- return randFromArray(firstNames);
-};
+ return randFromArray(firstNames)
+}
export const getRandomLastName = () => {
- return randFromArray(lastNames);
-};
+ return randFromArray(lastNames)
+}
export const getRandomFullName = () => {
- return `${getRandomFirstName()} ${getRandomLastName()}`;
-};
+ return `${getRandomFirstName()} ${getRandomLastName()}`
+}
diff --git a/data-gen/object-gen.test.ts b/data-gen/object-gen.test.ts
index 9104f81..6b1c8ce 100644
--- a/data-gen/object-gen.test.ts
+++ b/data-gen/object-gen.test.ts
@@ -1,89 +1,89 @@
-import { describe, expect, it } from "bun:test";
-import { dataGenerators, inferTypeAndGenerate } from "./object-gen";
+import { describe, expect, it } from 'bun:test'
+import { dataGenerators, inferTypeAndGenerate } from './object-gen'
-describe("dataGenerators", () => {
- it("should generate a random string", () => {
- const result = dataGenerators.string();
- expect(typeof result).toBe("string");
- });
+describe('dataGenerators', () => {
+ it('should generate a random string', () => {
+ const result = dataGenerators.string()
+ expect(typeof result).toBe('string')
+ })
- it("should generate a random number", () => {
- const result = dataGenerators.number();
- expect(typeof result).toBe("number");
- });
+ it('should generate a random number', () => {
+ const result = dataGenerators.number()
+ expect(typeof result).toBe('number')
+ })
- it("should generate a random boolean", () => {
- const result = dataGenerators.boolean();
- expect(typeof result).toBe("boolean");
- });
+ it('should generate a random boolean', () => {
+ const result = dataGenerators.boolean()
+ expect(typeof result).toBe('boolean')
+ })
- it("should generate a random date", () => {
- const result = dataGenerators.date();
- expect(result instanceof Date).toBe(true);
- });
+ it('should generate a random date', () => {
+ const result = dataGenerators.date()
+ expect(result instanceof Date).toBe(true)
+ })
- it("should generate a random object", () => {
+ it('should generate a random object', () => {
const result = dataGenerators.object({
name: dataGenerators.string,
age: dataGenerators.number,
isStudent: dataGenerators.boolean,
- })();
- expect(typeof result.name).toBe("string");
- expect(typeof result.age).toBe("number");
- expect(typeof result.isStudent).toBe("boolean");
- });
+ })()
+ expect(typeof result.name).toBe('string')
+ expect(typeof result.age).toBe('number')
+ expect(typeof result.isStudent).toBe('boolean')
+ })
- it("should generate a random array", () => {
- const result = dataGenerators.array(dataGenerators.number, 5)();
+ it('should generate a random array', () => {
+ const result = dataGenerators.array(dataGenerators.number, 5)()
- expect(Array.isArray(result)).toBe(true);
- expect(result.length).toBe(5);
- expect(typeof result[0]).toBe("number");
- });
-});
+ expect(Array.isArray(result)).toBe(true)
+ expect(result.length).toBe(5)
+ expect(typeof result[0]).toBe('number')
+ })
+})
-describe("inferTypeAndGenerate", () => {
- it("should generate a string for a string input", () => {
- const result = inferTypeAndGenerate("hello");
- expect(typeof result).toBe("string");
- });
+describe('inferTypeAndGenerate', () => {
+ it('should generate a string for a string input', () => {
+ const result = inferTypeAndGenerate('hello')
+ expect(typeof result).toBe('string')
+ })
- it("should generate a number for a number input", () => {
- const result = inferTypeAndGenerate(42);
+ it('should generate a number for a number input', () => {
+ const result = inferTypeAndGenerate(42)
// expect(result).toEqual(dataGenerators.number());
- expect(typeof result).toBe("number");
- });
+ expect(typeof result).toBe('number')
+ })
- it("should generate a boolean for a boolean input", () => {
- const result = inferTypeAndGenerate(true);
- expect(typeof result).toBe("boolean");
- });
+ it('should generate a boolean for a boolean input', () => {
+ const result = inferTypeAndGenerate(true)
+ expect(typeof result).toBe('boolean')
+ })
- it("should generate a date for a Date input", () => {
- const date = new Date();
- const result = inferTypeAndGenerate(date);
- expect(result instanceof Date).toBe(true);
- });
+ it('should generate a date for a Date input', () => {
+ const date = new Date()
+ const result = inferTypeAndGenerate(date)
+ expect(result instanceof Date).toBe(true)
+ })
- it("should generate an array for an array input", () => {
- const input = [1, 2, 3];
- const result = inferTypeAndGenerate(input);
- const inferredTypeGenerator = inferTypeAndGenerate(input[0]);
+ it('should generate an array for an array input', () => {
+ const input = [1, 2, 3]
+ const result = inferTypeAndGenerate(input)
+ const inferredTypeGenerator = inferTypeAndGenerate(input[0])
- expect(result.length).toEqual(input.length);
+ expect(result.length).toEqual(input.length)
- expect(typeof result[0]).toBe(typeof inferredTypeGenerator);
- expect(typeof input[0]).toEqual(typeof result[0]);
- });
+ expect(typeof result[0]).toBe(typeof inferredTypeGenerator)
+ expect(typeof input[0]).toEqual(typeof result[0])
+ })
- it("should generate an object for an object input", () => {
- const input = { name: "John", age: 30 };
- const result = inferTypeAndGenerate(input);
- const expected = dataGenerators.object(input)();
+ it('should generate an object for an object input', () => {
+ const input = { name: 'John', age: 30 }
+ const result = inferTypeAndGenerate(input)
+ const expected = dataGenerators.object(input)()
// validate that the types of each key value, match the expected key/value types
for (const key in expected) {
// @ts-ignore
- expect(typeof result[key]).toBe(typeof expected[key]);
+ expect(typeof result[key]).toBe(typeof expected[key])
}
- });
-});
+ })
+})
diff --git a/data-gen/object-gen.ts b/data-gen/object-gen.ts
index 2ab0586..bc4c673 100644
--- a/data-gen/object-gen.ts
+++ b/data-gen/object-gen.ts
@@ -1,19 +1,19 @@
-type DataGenerator = () => T;
+type DataGenerator = () => T
interface DataGenerators {
- string: (generator?: DataGenerator) => string;
- number: (generator?: DataGenerator) => number;
- boolean: (generator?: DataGenerator) => boolean;
- date: (generator?: DataGenerator) => Date;
+ string: (generator?: DataGenerator) => string
+ number: (generator?: DataGenerator) => number
+ boolean: (generator?: DataGenerator) => boolean
+ date: (generator?: DataGenerator) => Date
object: >(
shape: T,
generatorMap?: Partial>>,
- ) => DataGenerator;
- array: (generator: DataGenerator, length?: number) => DataGenerator;
+ ) => DataGenerator
+ array: (generator: DataGenerator, length?: number) => DataGenerator
}
export const dataGenerators: DataGenerators = {
- string: (generator = () => "Some random string") => generator(),
+ string: (generator = () => 'Some random string') => generator(),
number: (generator = () => Math.random() * 100) => generator(),
boolean: (generator = () => Math.random() < 0.5) => generator(),
date: (generator = () => new Date()) => generator(),
@@ -22,49 +22,49 @@ export const dataGenerators: DataGenerators = {
generatorMap: Partial>> = {},
) => {
return () => {
- const result = {} as any;
+ const result = {} as any
for (const key in shape) {
- const generator = generatorMap[key as keyof T] || shape[key];
- if (typeof generator === "function") {
- result[key] = generator();
+ const generator = generatorMap[key as keyof T] || shape[key]
+ if (typeof generator === 'function') {
+ result[key] = generator()
} else {
- result[key] = inferTypeAndGenerate(generator);
+ result[key] = inferTypeAndGenerate(generator)
}
}
- return result as T;
- };
+ return result as T
+ }
},
array: (generator: DataGenerator, length = 10) => {
return () => {
- const result = [];
+ const result = []
for (let i = 0; i < length; i++) {
- result.push(generator());
+ result.push(generator())
}
- return result;
- };
+ return result
+ }
},
-};
+}
export const inferTypeAndGenerate = (value: Val): any => {
switch (typeof value) {
- case "string":
- return dataGenerators.string();
- case "number":
- return dataGenerators.number();
- case "boolean":
- return dataGenerators.boolean();
+ case 'string':
+ return dataGenerators.string()
+ case 'number':
+ return dataGenerators.number()
+ case 'boolean':
+ return dataGenerators.boolean()
default:
if (value instanceof Date) {
- return dataGenerators.date();
+ return dataGenerators.date()
} else if (Array.isArray(value)) {
// Use first item in the array to infer type for array items
// This is a simplification and assumes uniform array types
- const inferredTypeGenerator = inferTypeAndGenerate(value[0]);
- return dataGenerators.array(() => inferredTypeGenerator, value.length)();
- } else if (typeof value === "object" && value !== null) {
- return dataGenerators.object(value as Record)();
+ const inferredTypeGenerator = inferTypeAndGenerate(value[0])
+ return dataGenerators.array(() => inferredTypeGenerator, value.length)()
+ } else if (typeof value === 'object' && value !== null) {
+ return dataGenerators.object(value as Record)()
}
}
- return null;
-};
+ return null
+}
diff --git a/data-gen/rand-date-range.test.ts b/data-gen/rand-date-range.test.ts
index e60fd62..a63c87b 100644
--- a/data-gen/rand-date-range.test.ts
+++ b/data-gen/rand-date-range.test.ts
@@ -1,13 +1,13 @@
-import { describe, expect, it } from "bun:test";
-import { randDateRange } from "./rand-date-range";
+import { describe, expect, it } from 'bun:test'
+import { randDateRange } from './rand-date-range'
-describe("randDateRange", () => {
- it("should return a date between the start and end dates", () => {
- const startDate = new Date("2021-01-01");
- const endDate = new Date("2021-12-31");
- const result = randDateRange(startDate, endDate);
- expect(result).toBeInstanceOf(Date);
- expect(result.getTime()).toBeGreaterThanOrEqual(startDate.getTime());
- expect(result.getTime()).toBeLessThanOrEqual(endDate.getTime());
- });
-});
+describe('randDateRange', () => {
+ it('should return a date between the start and end dates', () => {
+ const startDate = new Date('2021-01-01')
+ const endDate = new Date('2021-12-31')
+ const result = randDateRange(startDate, endDate)
+ expect(result).toBeInstanceOf(Date)
+ expect(result.getTime()).toBeGreaterThanOrEqual(startDate.getTime())
+ expect(result.getTime()).toBeLessThanOrEqual(endDate.getTime())
+ })
+})
diff --git a/data-gen/rand-date-range.ts b/data-gen/rand-date-range.ts
index c6b933a..e999580 100644
--- a/data-gen/rand-date-range.ts
+++ b/data-gen/rand-date-range.ts
@@ -1,6 +1,6 @@
export const randDateRange = (startDate: Date, endDate: Date): Date => {
- const start = startDate.getTime();
- const end = endDate.getTime();
- const randomDate = new Date(start + Math.random() * (end - start));
- return randomDate;
-};
+ const start = startDate.getTime()
+ const end = endDate.getTime()
+ const randomDate = new Date(start + Math.random() * (end - start))
+ return randomDate
+}
diff --git a/data-gen/rand-num.test.ts b/data-gen/rand-num.test.ts
index 4cc9beb..ab2631b 100644
--- a/data-gen/rand-num.test.ts
+++ b/data-gen/rand-num.test.ts
@@ -1,18 +1,18 @@
-import { describe, expect, it } from "bun:test";
-import { randFromArray, randNum } from "./rand-num";
+import { describe, expect, it } from 'bun:test'
+import { randFromArray, randNum } from './rand-num'
-describe("randNum", () => {
- it("should return a number between the min and max values", () => {
- const result = randNum(1, 10);
- expect(result).toBeGreaterThanOrEqual(1);
- expect(result).toBeLessThanOrEqual(10);
- });
-});
+describe('randNum', () => {
+ it('should return a number between the min and max values', () => {
+ const result = randNum(1, 10)
+ expect(result).toBeGreaterThanOrEqual(1)
+ expect(result).toBeLessThanOrEqual(10)
+ })
+})
-describe("randFromArray", () => {
- it("should return a random element from the array", () => {
- const arr = [1, 2, 3, 4, 5];
- const result = randFromArray(arr);
- expect(arr).toContain(result);
- });
-});
+describe('randFromArray', () => {
+ it('should return a random element from the array', () => {
+ const arr = [1, 2, 3, 4, 5]
+ const result = randFromArray(arr)
+ expect(arr).toContain(result)
+ })
+})
diff --git a/data-gen/rand-num.ts b/data-gen/rand-num.ts
index 835cfbb..4b74e94 100644
--- a/data-gen/rand-num.ts
+++ b/data-gen/rand-num.ts
@@ -1,7 +1,7 @@
export const randNum = (min: number, max: number) => {
- return Math.floor(Math.random() * (max - min + 1)) + min;
-};
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
export const randFromArray = (arr: T[]) => {
- return arr[randNum(0, arr.length - 1)];
-};
+ return arr[randNum(0, arr.length - 1)]
+}
diff --git a/data-gen/states.ts b/data-gen/states.ts
index ce5c8db..50b5934 100644
--- a/data-gen/states.ts
+++ b/data-gen/states.ts
@@ -1,113 +1,113 @@
-import { randFromArray } from "./rand-num";
+import { randFromArray } from './rand-num'
const uniqueStates = [
- "Alabama",
- "Alaska",
- "Arizona",
- "Arkansas",
- "California",
- "Colorado",
- "Connecticut",
- "Delaware",
- "Florida",
- "Georgia",
- "Hawaii",
- "Idaho",
- "Illinois",
- "Indiana",
- "Iowa",
- "Kansas",
- "Kentucky",
- "Louisiana",
- "Maine",
- "Maryland",
- "Massachusetts",
- "Michigan",
- "Minnesota",
- "Mississippi",
- "Missouri",
- "Montana",
- "Nebraska",
- "Nevada",
- "New Hampshire",
- "New Jersey",
- "New Mexico",
- "New York",
- "North Carolina",
- "North Dakota",
- "Ohio",
- "Oklahoma",
- "Oregon",
- "Pennsylvania",
- "Rhode Island",
- "South Carolina",
- "South Dakota",
- "Tennessee",
- "Texas",
- "Utah",
- "Vermont",
- "Virginia",
- "Washington",
- "West Virginia",
- "Wisconsin",
- "Wyoming",
- "American Samoa",
- "District of Columbia",
- "Guam",
- "Northern Mariana Islands",
- "Puerto Rico",
- "U.S. Virgin Islands",
- "Federated States of Micronesia",
- "Marshall Islands",
- "Palau",
- "Armed Forces Africa",
- "Armed Forces Americas",
- "Armed Forces Canada",
- "Armed Forces Europe",
- "Armed Forces Middle East",
- "Armed Forces Pacific",
- "Alberta",
- "British Columbia",
- "Manitoba",
- "New Brunswick",
- "Newfoundland and Labrador",
- "Northwest Territories",
- "Nova Scotia",
- "Nunavut",
- "Ontario",
- "Prince Edward Island",
- "Quebec",
- "Saskatchewan",
- "Yukon",
- "Johor",
- "Kedah",
- "Kelantan",
- "Melaka",
- "Negeri Sembilan",
- "Pahang",
- "Perak",
- "Perlis",
- "Pulau Pinang",
- "Sabah",
- "Sarawak",
- "Selangor",
- "Terengganu",
- "Baden-Württemberg",
- "Bavaria",
- "Berlin",
- "Brandenburg",
- "Bremen",
- "Hamburg",
- "Hesse",
- "Lower Saxony",
- "Mecklenburg-Vorpommern",
- "North Rhine-Westphalia",
- "Rhineland-Palatinate",
- "Saarland",
- "Saxony",
- "Saxony-Anhalt",
- "Schleswig-Holstein",
- "Thuringia",
-];
+ 'Alabama',
+ 'Alaska',
+ 'Arizona',
+ 'Arkansas',
+ 'California',
+ 'Colorado',
+ 'Connecticut',
+ 'Delaware',
+ 'Florida',
+ 'Georgia',
+ 'Hawaii',
+ 'Idaho',
+ 'Illinois',
+ 'Indiana',
+ 'Iowa',
+ 'Kansas',
+ 'Kentucky',
+ 'Louisiana',
+ 'Maine',
+ 'Maryland',
+ 'Massachusetts',
+ 'Michigan',
+ 'Minnesota',
+ 'Mississippi',
+ 'Missouri',
+ 'Montana',
+ 'Nebraska',
+ 'Nevada',
+ 'New Hampshire',
+ 'New Jersey',
+ 'New Mexico',
+ 'New York',
+ 'North Carolina',
+ 'North Dakota',
+ 'Ohio',
+ 'Oklahoma',
+ 'Oregon',
+ 'Pennsylvania',
+ 'Rhode Island',
+ 'South Carolina',
+ 'South Dakota',
+ 'Tennessee',
+ 'Texas',
+ 'Utah',
+ 'Vermont',
+ 'Virginia',
+ 'Washington',
+ 'West Virginia',
+ 'Wisconsin',
+ 'Wyoming',
+ 'American Samoa',
+ 'District of Columbia',
+ 'Guam',
+ 'Northern Mariana Islands',
+ 'Puerto Rico',
+ 'U.S. Virgin Islands',
+ 'Federated States of Micronesia',
+ 'Marshall Islands',
+ 'Palau',
+ 'Armed Forces Africa',
+ 'Armed Forces Americas',
+ 'Armed Forces Canada',
+ 'Armed Forces Europe',
+ 'Armed Forces Middle East',
+ 'Armed Forces Pacific',
+ 'Alberta',
+ 'British Columbia',
+ 'Manitoba',
+ 'New Brunswick',
+ 'Newfoundland and Labrador',
+ 'Northwest Territories',
+ 'Nova Scotia',
+ 'Nunavut',
+ 'Ontario',
+ 'Prince Edward Island',
+ 'Quebec',
+ 'Saskatchewan',
+ 'Yukon',
+ 'Johor',
+ 'Kedah',
+ 'Kelantan',
+ 'Melaka',
+ 'Negeri Sembilan',
+ 'Pahang',
+ 'Perak',
+ 'Perlis',
+ 'Pulau Pinang',
+ 'Sabah',
+ 'Sarawak',
+ 'Selangor',
+ 'Terengganu',
+ 'Baden-Württemberg',
+ 'Bavaria',
+ 'Berlin',
+ 'Brandenburg',
+ 'Bremen',
+ 'Hamburg',
+ 'Hesse',
+ 'Lower Saxony',
+ 'Mecklenburg-Vorpommern',
+ 'North Rhine-Westphalia',
+ 'Rhineland-Palatinate',
+ 'Saarland',
+ 'Saxony',
+ 'Saxony-Anhalt',
+ 'Schleswig-Holstein',
+ 'Thuringia',
+]
-export const getRandState = () => randFromArray(uniqueStates);
+export const getRandState = () => randFromArray(uniqueStates)
diff --git a/deploy/github-actions.ts b/deploy/github-actions.ts
index 74a5189..d72de47 100644
--- a/deploy/github-actions.ts
+++ b/deploy/github-actions.ts
@@ -1,87 +1,87 @@
-import { SyncSubprocess } from "bun";
-import { exit } from "process";
-import { ulog } from "../utils/ulog";
+import { SyncSubprocess } from 'bun'
+import { exit } from 'process'
+import { ulog } from '../utils/ulog'
export async function logStdOutput(proc: SyncSubprocess) {
try {
- const stdout = await new Response(proc.stdout).text();
- const stderr = proc?.stderr?.toString().trim();
+ const stdout = await new Response(proc.stdout).text()
+ const stderr = proc?.stderr?.toString().trim()
if (stdout) {
- ulog(stdout);
+ ulog(stdout)
}
if (stderr) {
- ulog(stderr);
+ ulog(stderr)
}
return {
stdout,
stderr,
- };
+ }
} catch (error) {
- console.error(error);
+ console.error(error)
}
}
type GHActions = {
- branch: "main" | "release";
- eventName: "push" | "pull_request";
-} & Omit;
+ branch: 'main' | 'release'
+ eventName: 'push' | 'pull_request'
+} & Omit
export function createGitHubActionsFactory({
sshRepoUrl,
}: {
- sshRepoUrl: string; // for now just ssh based
+ sshRepoUrl: string // for now just ssh based
}) {
const actionsEnv = {
eventName: Bun.env.GITHUB_EVENT_NAME,
runNumber: Bun.env.GITHUB_RUN_NUMBER,
- branch: Bun.env.GITHUB_REF?.split("/")?.[2],
+ branch: Bun.env.GITHUB_REF?.split('/')?.[2],
actor: Bun.env.GITHUB_ACTOR,
job: Bun.env.GITHUB_JOB,
refType: Bun.env.GITHUB_REF_TYPE,
- } as const;
+ } as const
const gitCmd = async (commands: string[], log = true) => {
- const commandArray = ["git", ...commands];
+ const commandArray = ['git', ...commands]
try {
- if (log) ulog(`Running command: ${commandArray.join(" ")}`);
+ if (log) ulog(`Running command: ${commandArray.join(' ')}`)
- const proc = Bun.spawnSync(commandArray);
+ const proc = Bun.spawnSync(commandArray)
- const stdout = await new Response(proc.stdout).text();
+ const stdout = await new Response(proc.stdout).text()
if (stdout.trim()) {
- ulog(stdout.trim());
+ ulog(stdout.trim())
}
- const stderr = await new Response(proc.stderr).text();
+ const stderr = await new Response(proc.stderr).text()
if (stderr.trim()) {
- console.error(stderr.trim());
+ console.error(stderr.trim())
}
} catch (error) {
- console.error(`Failed to run command: ${commandArray}:`, error);
- exit(1);
+ console.error(`Failed to run command: ${commandArray}:`, error)
+ exit(1)
}
- };
+ }
const commitAndPush = async (commitMsg: string) => {
- ulog("*** Running git commands ***");
+ ulog('*** Running git commands ***')
// Use the SSH to set the remote URL with authentication
- await gitCmd(["remote", "set-url", "origin", sshRepoUrl]);
- ulog("Configured GitHub User");
- await gitCmd(["config", "user.name"]);
+ await gitCmd(['remote', 'set-url', 'origin', sshRepoUrl])
+ ulog('Configured GitHub User')
+ await gitCmd(['config', 'user.name'])
- ulog("Configured GitHub Email");
- await gitCmd(["config", "user.email"]);
+ ulog('Configured GitHub Email')
+ await gitCmd(['config', 'user.email'])
- await gitCmd(["add", "."]);
- await gitCmd(["commit", "-m", `[skip ci] ${commitMsg}`]);
- await gitCmd(["push", "origin", "HEAD:main"]);
- };
+ await gitCmd(['add', '.'])
+ await gitCmd(['commit', '-m', `[skip ci] ${commitMsg}`])
+ await gitCmd(['push', 'origin', 'HEAD:main'])
+ }
const setupGitConfig = async (
{
@@ -89,31 +89,31 @@ export function createGitHubActionsFactory({
githubUsername,
}: {
// optional username and email for the git config, but defaults to the github-actions bot
- githubUsername: string;
- githubEmail: string;
+ githubUsername: string
+ githubEmail: string
} = {
- githubUsername: "github-actions[bot]",
- githubEmail: "41898282+github-actions[bot]@users.noreply.github.com",
+ githubUsername: 'github-actions[bot]',
+ githubEmail: '41898282+github-actions[bot]@users.noreply.github.com',
},
) => {
- ulog("*** Configuring Git ***");
+ ulog('*** Configuring Git ***')
// Configure user name and email
- ulog("Configuring GitHub User");
- await gitCmd(["config", "--global", "user.name", githubUsername]);
+ ulog('Configuring GitHub User')
+ await gitCmd(['config', '--global', 'user.name', githubUsername])
- ulog("Configured Git User: ", await gitCmd(["config", "user.name"]));
+ ulog('Configured Git User: ', await gitCmd(['config', 'user.name']))
- ulog("Configuring GitHub Email");
- await gitCmd(["config", "--global", "user.email", githubEmail]);
+ ulog('Configuring GitHub Email')
+ await gitCmd(['config', '--global', 'user.email', githubEmail])
- ulog("Configured Git Email: ", await gitCmd(["config", "user.email"]));
- };
+ ulog('Configured Git Email: ', await gitCmd(['config', 'user.email']))
+ }
return {
actionsEnv: actionsEnv as GHActions,
gitCmd,
commitAndPush,
setupGitConfig,
- };
+ }
}
diff --git a/deploy/index.ts b/deploy/index.ts
index fb630dc..7262f28 100644
--- a/deploy/index.ts
+++ b/deploy/index.ts
@@ -1 +1 @@
-export { createGitHubActionsFactory, logStdOutput } from "./github-actions";
+export { createGitHubActionsFactory, logStdOutput } from './github-actions'
diff --git a/fetcher/create-fetch-factory.test.ts b/fetcher/create-fetch-factory.test.ts
index 2b73a29..684d0d4 100644
--- a/fetcher/create-fetch-factory.test.ts
+++ b/fetcher/create-fetch-factory.test.ts
@@ -1,66 +1,66 @@
-import { describe, expect, test } from "bun:test";
-import { createFetchFactory } from "./create-fetch-factory";
+import { describe, expect, test } from 'bun:test'
+import { createFetchFactory } from './create-fetch-factory'
declare var global: {
- fetch: any;
-};
+ fetch: any
+}
type FetchArgs = {
- url: string;
- options: RequestInit;
-};
+ url: string
+ options: RequestInit
+}
-describe("post method", () => {
- test("should make a post request to the correct URL with JSON body", async () => {
+describe('post method', () => {
+ test('should make a post request to the correct URL with JSON body', async () => {
let fetchArgs: FetchArgs = {
- url: "",
+ url: '',
options: {},
- };
+ }
global.fetch = async (url: string, options: RequestInit) => {
- fetchArgs = { url, options };
+ fetchArgs = { url, options }
return {
ok: true,
- json: async () => ({ message: "Success" }),
- };
- };
+ json: async () => ({ message: 'Success' }),
+ }
+ }
- const headers = new Headers();
+ const headers = new Headers()
- headers.set("Authorization", "Bearer token");
+ headers.set('Authorization', 'Bearer token')
const fetchFactory = createFetchFactory({
- baseUrl: "https://api.example.com",
+ baseUrl: 'https://api.example.com',
defaultHeaders: headers,
config: {
test: {
- endpoint: "/test",
- method: "POST",
+ endpoint: '/test',
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
},
},
- });
- const postData = { key: "value" };
- await fetchFactory.post({ endpoint: "test", body: postData });
+ })
+ const postData = { key: 'value' }
+ await fetchFactory.post({ endpoint: 'test', body: postData })
- expect(fetchArgs.url).toBe("https://api.example.com/test");
- expect(fetchArgs.options.method).toBe("POST");
+ expect(fetchArgs.url).toBe('https://api.example.com/test')
+ expect(fetchArgs.options.method).toBe('POST')
// TODO: fix header tests
// expect(fetchArgs.options.headers.get("Content-Type")).toBe(
// "application/json"
// );
// expect(fetchArgs.options.headers.get("Authorization")).toBe("Bearer token");
- expect(fetchArgs.options.body).toBe(JSON.stringify(postData));
- });
-});
+ expect(fetchArgs.options.body).toBe(JSON.stringify(postData))
+ })
+})
-describe("postForm method", () => {
- test("should make a post request to the correct URL with FormData body", async () => {
+describe('postForm method', () => {
+ test('should make a post request to the correct URL with FormData body', async () => {
let fetchArgs: FetchArgs = {
- url: "",
+ url: '',
options: {},
- };
+ }
global.fetch = async (url: string, options: RequestInit) => {
fetchArgs = {
url,
@@ -68,60 +68,60 @@ describe("postForm method", () => {
...options,
headers: new Headers(options.headers),
},
- };
+ }
return {
ok: true,
- json: async () => ({ message: "Success" }),
- };
- };
+ json: async () => ({ message: 'Success' }),
+ }
+ }
const fetchFactory = createFetchFactory({
- baseUrl: "https://api.example.com",
+ baseUrl: 'https://api.example.com',
config: {
test: {
- endpoint: "/test",
- method: "POST",
+ endpoint: '/test',
+ method: 'POST',
},
},
- });
- const formData = new FormData();
- formData.append("key", "value");
+ })
+ const formData = new FormData()
+ formData.append('key', 'value')
// @ts-expect-error
- await fetchFactory.postForm({ endpoint: "test", bodyData: formData });
+ await fetchFactory.postForm({ endpoint: 'test', bodyData: formData })
- expect(fetchArgs.url).toBe("https://api.example.com/test");
- expect(fetchArgs.options.method).toBe("POST");
+ expect(fetchArgs.url).toBe('https://api.example.com/test')
+ expect(fetchArgs.options.method).toBe('POST')
// @ts-expect-error
- expect(fetchArgs?.options?.headers?.get(["content-type"])).toContain("multipart/form-data");
- });
-});
+ expect(fetchArgs?.options?.headers?.get(['content-type'])).toContain('multipart/form-data')
+ })
+})
-describe("delete method", () => {
- test("should make a delete request to the correct URL", async () => {
+describe('delete method', () => {
+ test('should make a delete request to the correct URL', async () => {
let fetchArgs: FetchArgs = {
- url: "",
+ url: '',
options: {},
- };
+ }
global.fetch = async (url: string, options: RequestInit) => {
- fetchArgs = { url, options };
+ fetchArgs = { url, options }
return {
ok: true,
- json: async () => ({ message: "Success" }),
- };
- };
+ json: async () => ({ message: 'Success' }),
+ }
+ }
const fetchFactory = createFetchFactory({
- baseUrl: "https://api.example.com",
+ baseUrl: 'https://api.example.com',
config: {
test: {
- endpoint: "/test",
- method: "delete",
+ endpoint: '/test',
+ method: 'delete',
},
},
- });
- await fetchFactory.delete({ endpoint: "test" });
+ })
+ await fetchFactory.delete({ endpoint: 'test' })
- expect(fetchArgs.url).toBe("https://api.example.com/test");
- expect(fetchArgs.options.method).toBe("DELETE");
- });
-});
+ expect(fetchArgs.url).toBe('https://api.example.com/test')
+ expect(fetchArgs.options.method).toBe('DELETE')
+ })
+})
diff --git a/fetcher/create-fetch-factory.ts b/fetcher/create-fetch-factory.ts
index f3d2f9a..7aad073 100644
--- a/fetcher/create-fetch-factory.ts
+++ b/fetcher/create-fetch-factory.ts
@@ -1,105 +1,105 @@
-declare var window: any;
+declare var window: any
declare var document: {
- createElement: any;
+ createElement: any
body: {
- appendChild: any;
- removeChild: any;
- };
-};
-import { HTTPMethod } from "../utils/http-types";
-import { ExternalFetchConfig, MappedApiConfig, TypeMap } from "./fetch-types";
-import { computeHeaders, createEventStream, fetcher, fileDownload } from "./fetch-utils";
+ appendChild: any
+ removeChild: any
+ }
+}
+import { HTTPMethod } from '../utils/http-types'
+import { ExternalFetchConfig, MappedApiConfig, TypeMap } from './fetch-types'
+import { computeHeaders, createEventStream, fetcher, fileDownload } from './fetch-utils'
-export type FactoryMethods = keyof ReturnType;
+export type FactoryMethods = keyof ReturnType
export function createFetchFactory({
- baseUrl = "",
+ baseUrl = '',
config,
defaultHeaders,
debug = false,
}: {
- baseUrl?: string;
- debug?: boolean;
- config: Record>;
- defaultHeaders?: Headers; // Headers can be strings or functions returning strings
+ baseUrl?: string
+ debug?: boolean
+ config: Record>
+ defaultHeaders?: Headers // Headers can be strings or functions returning strings
}) {
return {
fetcher: (
fetcherConfig: ExternalFetchConfig,
- ): Promise => {
- const headers = computeHeaders(defaultHeaders || {}, fetcherConfig.headers || {});
+ ): Promise => {
+ const headers = computeHeaders(defaultHeaders || {}, fetcherConfig.headers || {})
return fetcher(
{
...fetcherConfig,
headers,
- method: fetcherConfig.method || "GET",
+ method: fetcherConfig.method || 'GET',
},
config,
baseUrl,
- );
+ )
},
get: (
- fetcherConfig: ExternalFetchConfig,
- ): Promise => {
- const headers = computeHeaders(defaultHeaders || {}, fetcherConfig.headers || {});
+ fetcherConfig: ExternalFetchConfig,
+ ): Promise => {
+ const headers = computeHeaders(defaultHeaders || {}, fetcherConfig.headers || {})
return fetcher(
{
...fetcherConfig,
headers,
- method: "GET",
+ method: 'GET',
},
config,
baseUrl,
- );
+ )
},
post: (
- fetchConfig: ExternalFetchConfig & {
- endpoint: Endpoint;
+ fetchConfig: ExternalFetchConfig & {
+ endpoint: Endpoint
},
- ): Promise => {
- const headers = computeHeaders(defaultHeaders || {}, fetchConfig.headers || {});
+ ): Promise => {
+ const headers = computeHeaders(defaultHeaders || {}, fetchConfig.headers || {})
- return fetcher({ ...fetchConfig, headers, method: "POST" }, config, baseUrl);
+ return fetcher({ ...fetchConfig, headers, method: 'POST' }, config, baseUrl)
},
postForm: (
- fetchConfig: ExternalFetchConfig & {
- endpoint: Endpoint;
- boundary?: string;
+ fetchConfig: ExternalFetchConfig & {
+ endpoint: Endpoint
+ boundary?: string
},
- ): Promise => {
+ ): Promise => {
const defaultContentType = fetchConfig.boundary
? `multipart/form-data; boundary=${fetchConfig.boundary}`
- : "multipart/form-data";
+ : 'multipart/form-data'
const formHeaders = {
- "Content-Type": defaultContentType,
+ 'Content-Type': defaultContentType,
...fetchConfig.headers,
- };
- const headers = computeHeaders(defaultHeaders || {}, formHeaders || {});
+ }
+ const headers = computeHeaders(defaultHeaders || {}, formHeaders || {})
return fetcher(
{
...fetchConfig,
headers,
- method: "POST",
+ method: 'POST',
},
config,
baseUrl,
- );
+ )
},
delete: (
- fetchConfig: ExternalFetchConfig & {
- endpoint: Endpoint;
+ fetchConfig: ExternalFetchConfig & {
+ endpoint: Endpoint
},
- ): Promise => {
- const headers = computeHeaders(defaultHeaders || {}, fetchConfig.headers || {});
- return fetcher({ ...fetchConfig, headers, method: "DELETE" }, config, baseUrl);
+ ): Promise => {
+ const headers = computeHeaders(defaultHeaders || {}, fetchConfig.headers || {})
+ return fetcher({ ...fetchConfig, headers, method: 'DELETE' }, config, baseUrl)
},
createEventStream,
fileDownload,
- };
+ }
}
diff --git a/fetcher/fetch-types.ts b/fetcher/fetch-types.ts
index 0663f42..6727ca7 100644
--- a/fetcher/fetch-types.ts
+++ b/fetcher/fetch-types.ts
@@ -1,38 +1,38 @@
-import { HTTPMethod, RouteMethods } from "../utils/http-types";
+import { HTTPMethod, RouteMethods } from '../utils/http-types'
-export type EventHandlerMap = { [event: string]: (ev: MessageEvent) => void };
+export type EventHandlerMap = { [event: string]: (ev: MessageEvent) => void }
export type APIConfig = {
- method: RouteMethods;
- endpoint: string;
- response?: TRes;
- params?: TParams;
- body?: TBody;
- headers?: THeaders;
-};
+ method: RouteMethods
+ endpoint: string
+ response?: TRes
+ params?: TParams
+ body?: TBody
+ headers?: THeaders
+}
export type TypeMap = {
- [endpoint: string | number]: APIConfig;
-};
+ [endpoint: string | number]: APIConfig
+}
export type FileDownloadConfig = {
- endpoint: string;
- headers?: HeadersInit;
- filename?: string;
- params?: Record;
-};
+ endpoint: string
+ headers?: HeadersInit
+ filename?: string
+ params?: Record
+}
export type MappedApiConfig = APIConfig<
- TMap[keyof TMap]["response"],
- TMap[keyof TMap]["params"],
- TMap[keyof TMap]["body"],
+ TMap[keyof TMap]['response'],
+ TMap[keyof TMap]['params'],
+ TMap[keyof TMap]['body'],
HeadersInit
->;
+>
export type ExternalFetchConfig = Omit<
MappedApiConfig,
- "response" | "method"
+ 'response' | 'method'
> & {
- endpoint: Endpoint;
- method?: Method;
-};
+ endpoint: Endpoint
+ method?: Method
+}
diff --git a/fetcher/fetch-utils.ts b/fetcher/fetch-utils.ts
index d7ef2bd..55ceeb8 100644
--- a/fetcher/fetch-utils.ts
+++ b/fetcher/fetch-utils.ts
@@ -1,47 +1,47 @@
-import { EventHandlerMap, ExternalFetchConfig, FileDownloadConfig, MappedApiConfig, TypeMap } from "./fetch-types";
+import { EventHandlerMap, ExternalFetchConfig, FileDownloadConfig, MappedApiConfig, TypeMap } from './fetch-types'
declare var window: {
- fetch: any;
-};
+ fetch: any
+}
declare var document: {
- createElement: any;
- body: any;
-};
+ createElement: any
+ body: any
+}
export function appendURLParameters(url: string, params: Record = {}): string {
- const urlWithParams = new URLSearchParams();
+ const urlWithParams = new URLSearchParams()
Object.entries(params).forEach(([key, value]) => {
- urlWithParams.append(key, value);
- });
- return urlWithParams.toString() ? `${url}?${urlWithParams.toString()}` : url;
+ urlWithParams.append(key, value)
+ })
+ return urlWithParams.toString() ? `${url}?${urlWithParams.toString()}` : url
}
async function handleResponse(response: Response): Promise {
if (!response.ok) {
- throw new Error(JSON.stringify(response));
+ throw new Error(JSON.stringify(response))
}
- return await response.json();
+ return await response.json()
}
export function computeHeaders(defaultHeaders: HeadersInit, customHeaders?: HeadersInit): HeadersInit {
- const resultHeaders = new Headers(defaultHeaders);
+ const resultHeaders = new Headers(defaultHeaders)
if (customHeaders instanceof Headers) {
for (const [key, value] of customHeaders.entries()) {
- resultHeaders.set(key, value);
+ resultHeaders.set(key, value)
}
} else if (Array.isArray(customHeaders)) {
customHeaders.forEach(([key, value]) => {
- resultHeaders.set(key, value);
- });
+ resultHeaders.set(key, value)
+ })
} else {
for (const [key, value] of Object.entries(customHeaders || {})) {
- resultHeaders.set(key, value as string);
+ resultHeaders.set(key, value as string)
}
}
- return resultHeaders;
+ return resultHeaders
}
export async function fetcher(
@@ -49,58 +49,58 @@ export async function fetcher
config: Record>,
baseUrl: string,
// computeHeadersFunction: (headers?: HeadersInit) => HeadersInit
-): Promise {
- const endpointConfig = config[fetcherConfig.endpoint];
- const finalUrl = appendURLParameters(baseUrl + endpointConfig.endpoint, fetcherConfig.params);
+): Promise {
+ const endpointConfig = config[fetcherConfig.endpoint]
+ const finalUrl = appendURLParameters(baseUrl + endpointConfig.endpoint, fetcherConfig.params)
- const method = endpointConfig.method;
- let bodyData = "";
+ const method = endpointConfig.method
+ let bodyData = ''
if (fetcherConfig.body) {
- bodyData = JSON.stringify(fetcherConfig.body);
+ bodyData = JSON.stringify(fetcherConfig.body)
}
const response = await fetch(finalUrl, {
method: method.toUpperCase(),
headers: fetcherConfig?.headers,
body: bodyData,
- });
+ })
- return handleResponse(response);
+ return handleResponse(response)
}
export function fileDownload(config: FileDownloadConfig, baseUrl: string): void {
- if (typeof window === "undefined") return;
- const finalUrl = new URL(baseUrl + config.endpoint);
+ if (typeof window === 'undefined') return
+ const finalUrl = new URL(baseUrl + config.endpoint)
if (config.params) {
Object.keys(config.params).forEach((key) => {
- finalUrl.searchParams.append(key, config.params![key]);
- });
+ finalUrl.searchParams.append(key, config.params![key])
+ })
}
- const a = document.createElement("a");
- a.href = finalUrl.toString();
- a.download = config.filename || "";
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
+ const a = document.createElement('a')
+ a.href = finalUrl.toString()
+ a.download = config.filename || ''
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
}
export function createEventStream(endpoint: string, eventHandlers: EventHandlerMap, baseUrl: string): EventSource {
- const url = baseUrl + endpoint;
- const es = new EventSource(url);
+ const url = baseUrl + endpoint
+ const es = new EventSource(url)
es.onopen = (event) => {
- console.info("Stream opened:", event);
- };
+ console.info('Stream opened:', event)
+ }
es.onerror = (error) => {
- console.error("Stream Error:", error);
- };
+ console.error('Stream Error:', error)
+ }
for (const [event, handler] of Object.entries(eventHandlers)) {
- es.addEventListener(event as string, handler);
+ es.addEventListener(event as string, handler)
}
- return es;
+ return es
}
diff --git a/fetcher/index.ts b/fetcher/index.ts
index 88a0151..df883df 100644
--- a/fetcher/index.ts
+++ b/fetcher/index.ts
@@ -1 +1 @@
-export { createFetchFactory } from "./create-fetch-factory";
+export { createFetchFactory } from './create-fetch-factory'
diff --git a/files-folders/file-editing-utils.test.ts b/files-folders/file-editing-utils.test.ts
index fdd07a1..79c48e3 100644
--- a/files-folders/file-editing-utils.test.ts
+++ b/files-folders/file-editing-utils.test.ts
@@ -1,30 +1,30 @@
-import { describe, expect, it } from "bun:test";
-import { saveOrUpdateFile } from "./file-editing-utils";
+import { describe, expect, it } from 'bun:test'
+import { saveOrUpdateFile } from './file-editing-utils'
-import { readFileContent } from "./file-reading-utils";
-import { deletePath } from "./file-validation-utils";
+import { readFileContent } from './file-reading-utils'
+import { deletePath } from './file-validation-utils'
-const savePath = process.env.PWD + "/files-folders/test";
+const savePath = process.env.PWD + '/files-folders/test'
-describe("saveResultToFile", () => {
- it("should save and update content to file", async () => {
- const saveFile = savePath + "/test.text";
+describe('saveResultToFile', () => {
+ it('should save and update content to file', async () => {
+ const saveFile = savePath + '/test.text'
- const content = "Hello, world!";
+ const content = 'Hello, world!'
- await saveOrUpdateFile({ filePath: saveFile, content });
+ await saveOrUpdateFile({ filePath: saveFile, content })
- const fileContent = await readFileContent(saveFile);
+ const fileContent = await readFileContent(saveFile)
- expect(fileContent).toEqual(content);
+ expect(fileContent).toEqual(content)
- const newFileContent = "Goodbye, world!";
- await saveOrUpdateFile({ filePath: saveFile, content: newFileContent });
+ const newFileContent = 'Goodbye, world!'
+ await saveOrUpdateFile({ filePath: saveFile, content: newFileContent })
- const newReadFileContent = await readFileContent(saveFile);
+ const newReadFileContent = await readFileContent(saveFile)
- expect(newReadFileContent).toEqual(newFileContent);
+ expect(newReadFileContent).toEqual(newFileContent)
- await deletePath(saveFile);
- });
-});
+ await deletePath(saveFile)
+ })
+})
diff --git a/files-folders/file-editing-utils.ts b/files-folders/file-editing-utils.ts
index fe3801b..46ca858 100644
--- a/files-folders/file-editing-utils.ts
+++ b/files-folders/file-editing-utils.ts
@@ -1,30 +1,30 @@
-import fsPromise from "fs/promises";
-import path from "path";
-import { directoryExists } from "./file-validation-utils";
+import fsPromise from 'fs/promises'
+import path from 'path'
+import { directoryExists } from './file-validation-utils'
type SaveOptions = {
- filePath: string;
- content: string | object;
- isJson?: boolean;
-};
+ filePath: string
+ content: string | object
+ isJson?: boolean
+}
export async function saveOrUpdateFile({ filePath, content, isJson = false }: SaveOptions): Promise {
try {
- const dirPath = path.dirname(filePath);
- await directoryExists({ path: dirPath, createMissingDirs: true });
+ const dirPath = path.dirname(filePath)
+ await directoryExists({ path: dirPath, createMissingDirs: true })
- let dataToWrite = content;
+ let dataToWrite = content
if (isJson) {
- dataToWrite = JSON.stringify(content, null, 2);
+ dataToWrite = JSON.stringify(content, null, 2)
}
- await fsPromise.writeFile(filePath, dataToWrite as string);
+ await fsPromise.writeFile(filePath, dataToWrite as string)
} catch (err: any) {
- throw new Error(`saveOrUpdateFile: Failed to save or update file at path ${filePath}: ${err?.message}`);
+ throw new Error(`saveOrUpdateFile: Failed to save or update file at path ${filePath}: ${err?.message}`)
}
}
export async function updateMultipleFiles(filePaths: string[], content: string): Promise {
- return Promise.all(filePaths.map((filePath) => saveOrUpdateFile({ filePath, content })));
+ return Promise.all(filePaths.map((filePath) => saveOrUpdateFile({ filePath, content })))
}
diff --git a/files-folders/file-extension-map.ts b/files-folders/file-extension-map.ts
index 66699a3..d26c763 100644
--- a/files-folders/file-extension-map.ts
+++ b/files-folders/file-extension-map.ts
@@ -1,274 +1,274 @@
export type FileExtensionType = {
- name: string;
- description: string;
- logoUrl: string;
- mime: string;
- encoding: string;
-};
+ name: string
+ description: string
+ logoUrl: string
+ mime: string
+ encoding: string
+}
const defaultExtensionInfo: FileExtensionType = {
- name: "Unknown",
- description: "Unknown",
- logoUrl: "",
- mime: "",
- encoding: "",
-};
+ name: 'Unknown',
+ description: 'Unknown',
+ logoUrl: '',
+ mime: '',
+ encoding: '',
+}
type ExtensionMap = {
- [key: string]: FileExtensionType;
-};
+ [key: string]: FileExtensionType
+}
export const fileExtensionMap = {
ts: {
- name: "TypeScript",
- description: "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.",
- logoUrl: "https://raw.githubusercontent.com/github/explore/master/topics/typescript/typescript.png",
- mime: "application/typescript",
- encoding: "utf-8",
+ name: 'TypeScript',
+ description: 'TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.',
+ logoUrl: 'https://raw.githubusercontent.com/github/explore/master/topics/typescript/typescript.png',
+ mime: 'application/typescript',
+ encoding: 'utf-8',
},
tsx: {
- name: "TypeScript JSX",
- description: "Used with React in TypeScript projects.",
- logoUrl: "",
- mime: "application/typescript",
- encoding: "utf-8",
+ name: 'TypeScript JSX',
+ description: 'Used with React in TypeScript projects.',
+ logoUrl: '',
+ mime: 'application/typescript',
+ encoding: 'utf-8',
},
js: {
- name: "JavaScript",
- description: "A lightweight, interpreted, or just-in-time compiled programming language.",
- logoUrl: "",
- mime: "application/javascript",
- encoding: "utf-8",
+ name: 'JavaScript',
+ description: 'A lightweight, interpreted, or just-in-time compiled programming language.',
+ logoUrl: '',
+ mime: 'application/javascript',
+ encoding: 'utf-8',
},
jsx: {
- name: "JavaScript JSX",
- description: "Used with React in JavaScript projects.",
- logoUrl: "",
- mime: "application/javascript",
- encoding: "utf-8",
+ name: 'JavaScript JSX',
+ description: 'Used with React in JavaScript projects.',
+ logoUrl: '',
+ mime: 'application/javascript',
+ encoding: 'utf-8',
},
json: {
- name: "JSON",
- description: "JavaScript Object Notation.",
- logoUrl: "",
- mime: "application/json",
- encoding: "utf-8",
+ name: 'JSON',
+ description: 'JavaScript Object Notation.',
+ logoUrl: '',
+ mime: 'application/json',
+ encoding: 'utf-8',
},
toml: {
- name: "TOML",
+ name: 'TOML',
description: "Tom's Obvious, Minimal Language.",
- logoUrl: "",
- mime: "application/toml",
- encoding: "utf-8",
+ logoUrl: '',
+ mime: 'application/toml',
+ encoding: 'utf-8',
},
md: {
- name: "Markdown",
- description: "A lightweight markup language.",
- logoUrl: "",
- mime: "text/markdown",
- encoding: "utf-8",
+ name: 'Markdown',
+ description: 'A lightweight markup language.',
+ logoUrl: '',
+ mime: 'text/markdown',
+ encoding: 'utf-8',
},
html: {
- name: "HTML",
- description: "Hypertext Markup Language.",
- logoUrl: "",
- mime: "text/html",
- encoding: "utf-8",
+ name: 'HTML',
+ description: 'Hypertext Markup Language.',
+ logoUrl: '',
+ mime: 'text/html',
+ encoding: 'utf-8',
},
css: {
- name: "CSS",
- description: "Cascading Style Sheets.",
- logoUrl: "",
- mime: "text/css",
- encoding: "utf-8",
+ name: 'CSS',
+ description: 'Cascading Style Sheets.',
+ logoUrl: '',
+ mime: 'text/css',
+ encoding: 'utf-8',
},
cpp: {
- name: "C++",
- description: "C++ programming language.",
- logoUrl: "",
- mime: "text/x-c++",
- encoding: "utf-8",
+ name: 'C++',
+ description: 'C++ programming language.',
+ logoUrl: '',
+ mime: 'text/x-c++',
+ encoding: 'utf-8',
},
rs: {
- name: "Rust",
- description: "A language empowering everyone to build reliable and efficient software.",
- logoUrl: "",
- mime: "text/rust",
- encoding: "utf-8",
+ name: 'Rust',
+ description: 'A language empowering everyone to build reliable and efficient software.',
+ logoUrl: '',
+ mime: 'text/rust',
+ encoding: 'utf-8',
},
c: {
- name: "C",
- description: "C programming language.",
- logoUrl: "",
- mime: "text/x-c",
- encoding: "utf-8",
+ name: 'C',
+ description: 'C programming language.',
+ logoUrl: '',
+ mime: 'text/x-c',
+ encoding: 'utf-8',
},
py: {
- name: "Python",
- description: "A high-level, interpreted scripting language.",
- logoUrl: "",
- mime: "text/x-python",
- encoding: "utf-8",
+ name: 'Python',
+ description: 'A high-level, interpreted scripting language.',
+ logoUrl: '',
+ mime: 'text/x-python',
+ encoding: 'utf-8',
},
txt: {
- name: "Text",
- description: "Plain text file.",
- logoUrl: "",
- mime: "text/plain",
- encoding: "utf-8",
+ name: 'Text',
+ description: 'Plain text file.',
+ logoUrl: '',
+ mime: 'text/plain',
+ encoding: 'utf-8',
},
jpeg: {
- name: "JPEG",
- description: "JPEG image format.",
- logoUrl: "",
- mime: "image/jpeg",
- encoding: "",
+ name: 'JPEG',
+ description: 'JPEG image format.',
+ logoUrl: '',
+ mime: 'image/jpeg',
+ encoding: '',
},
jpg: {
- name: "JPG",
- description: "JPG image format.",
- logoUrl: "",
- mime: "image/jpeg",
- encoding: "",
+ name: 'JPG',
+ description: 'JPG image format.',
+ logoUrl: '',
+ mime: 'image/jpeg',
+ encoding: '',
},
png: {
- name: "PNG",
- description: "Portable Network Graphics.",
- logoUrl: "",
- mime: "image/png",
- encoding: "",
+ name: 'PNG',
+ description: 'Portable Network Graphics.',
+ logoUrl: '',
+ mime: 'image/png',
+ encoding: '',
},
zip: {
- name: "ZIP",
- description: "ZIP file format.",
- logoUrl: "",
- mime: "application/zip",
- encoding: "",
+ name: 'ZIP',
+ description: 'ZIP file format.',
+ logoUrl: '',
+ mime: 'application/zip',
+ encoding: '',
},
gzip: {
- name: "GZIP",
- description: "GNU zip, a file compression format.",
- logoUrl: "",
- mime: "application/gzip",
- encoding: "",
+ name: 'GZIP',
+ description: 'GNU zip, a file compression format.',
+ logoUrl: '',
+ mime: 'application/gzip',
+ encoding: '',
},
xml: {
- name: "XML",
- description: "eXtensible Markup Language.",
- logoUrl: "",
- mime: "application/xml",
- encoding: "utf-8",
+ name: 'XML',
+ description: 'eXtensible Markup Language.',
+ logoUrl: '',
+ mime: 'application/xml',
+ encoding: 'utf-8',
},
svg: {
- name: "SVG",
- description: "Scalable Vector Graphics.",
- logoUrl: "",
- mime: "image/svg+xml",
- encoding: "utf-8",
+ name: 'SVG',
+ description: 'Scalable Vector Graphics.',
+ logoUrl: '',
+ mime: 'image/svg+xml',
+ encoding: 'utf-8',
},
gif: {
- name: "GIF",
- description: "Graphics Interchange Format.",
- logoUrl: "",
- mime: "image/gif",
- encoding: "",
+ name: 'GIF',
+ description: 'Graphics Interchange Format.',
+ logoUrl: '',
+ mime: 'image/gif',
+ encoding: '',
},
pdf: {
- name: "PDF",
- description: "Portable Document Format.",
- logoUrl: "",
- mime: "application/pdf",
- encoding: "",
+ name: 'PDF',
+ description: 'Portable Document Format.',
+ logoUrl: '',
+ mime: 'application/pdf',
+ encoding: '',
},
mp3: {
- name: "MP3",
- description: "Audio file format.",
- logoUrl: "",
- mime: "audio/mpeg",
- encoding: "",
+ name: 'MP3',
+ description: 'Audio file format.',
+ logoUrl: '',
+ mime: 'audio/mpeg',
+ encoding: '',
},
mp4: {
- name: "MP4",
- description: "Video file format.",
- logoUrl: "",
- mime: "video/mp4",
- encoding: "",
+ name: 'MP4',
+ description: 'Video file format.',
+ logoUrl: '',
+ mime: 'video/mp4',
+ encoding: '',
},
doc: {
- name: "DOC",
- description: "Microsoft Word document.",
- logoUrl: "",
- mime: "application/msword",
- encoding: "",
+ name: 'DOC',
+ description: 'Microsoft Word document.',
+ logoUrl: '',
+ mime: 'application/msword',
+ encoding: '',
},
docx: {
- name: "DOCX",
- description: "Microsoft Word Open XML document.",
- logoUrl: "",
- mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- encoding: "",
+ name: 'DOCX',
+ description: 'Microsoft Word Open XML document.',
+ logoUrl: '',
+ mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ encoding: '',
},
xls: {
- name: "XLS",
- description: "Microsoft Excel spreadsheet.",
- logoUrl: "",
- mime: "application/vnd.ms-excel",
- encoding: "",
+ name: 'XLS',
+ description: 'Microsoft Excel spreadsheet.',
+ logoUrl: '',
+ mime: 'application/vnd.ms-excel',
+ encoding: '',
},
xlsx: {
- name: "XLSX",
- description: "Microsoft Excel Open XML spreadsheet.",
- logoUrl: "",
- mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
- encoding: "",
+ name: 'XLSX',
+ description: 'Microsoft Excel Open XML spreadsheet.',
+ logoUrl: '',
+ mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ encoding: '',
},
ppt: {
- name: "PPT",
- description: "Microsoft PowerPoint presentation.",
- logoUrl: "",
- mime: "application/vnd.ms-powerpoint",
- encoding: "",
+ name: 'PPT',
+ description: 'Microsoft PowerPoint presentation.',
+ logoUrl: '',
+ mime: 'application/vnd.ms-powerpoint',
+ encoding: '',
},
pptx: {
- name: "PPTX",
- description: "Microsoft PowerPoint Open XML presentation.",
- logoUrl: "",
- mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
- encoding: "",
+ name: 'PPTX',
+ description: 'Microsoft PowerPoint Open XML presentation.',
+ logoUrl: '',
+ mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ encoding: '',
},
odt: {
- name: "ODT",
- description: "OpenDocument Text Document.",
- logoUrl: "",
- mime: "application/vnd.oasis.opendocument.text",
- encoding: "",
+ name: 'ODT',
+ description: 'OpenDocument Text Document.',
+ logoUrl: '',
+ mime: 'application/vnd.oasis.opendocument.text',
+ encoding: '',
},
ods: {
- name: "ODS",
- description: "OpenDocument Spreadsheet Document.",
- logoUrl: "",
- mime: "application/vnd.oasis.opendocument.spreadsheet",
- encoding: "",
+ name: 'ODS',
+ description: 'OpenDocument Spreadsheet Document.',
+ logoUrl: '',
+ mime: 'application/vnd.oasis.opendocument.spreadsheet',
+ encoding: '',
},
odp: {
- name: "ODP",
- description: "OpenDocument Presentation Document.",
- logoUrl: "",
- mime: "application/vnd.oasis.opendocument.presentation",
- encoding: "",
+ name: 'ODP',
+ description: 'OpenDocument Presentation Document.',
+ logoUrl: '',
+ mime: 'application/vnd.oasis.opendocument.presentation',
+ encoding: '',
},
-} satisfies ExtensionMap;
+} satisfies ExtensionMap
-export type ExtensionMapKeys = keyof typeof fileExtensionMap;
+export type ExtensionMapKeys = keyof typeof fileExtensionMap
export const fullMimeForExtension = (extension: ExtensionMapKeys) => {
- let extensionType = fileExtensionMap[extension] ?? defaultExtensionInfo;
+ let extensionType = fileExtensionMap[extension] ?? defaultExtensionInfo
// If there's no encoding specified, return just the MIME type.
if (!extensionType.encoding) {
- return extensionType.mime;
+ return extensionType.mime
}
// Otherwise, return MIME type with encoding.
- return `${extensionType.mime};charset=${extensionType.encoding}`;
-};
+ return `${extensionType.mime};charset=${extensionType.encoding}`
+}
diff --git a/files-folders/file-factory.ts b/files-folders/file-factory.ts
index de0dae2..0c4f74f 100644
--- a/files-folders/file-factory.ts
+++ b/files-folders/file-factory.ts
@@ -1,8 +1,8 @@
-import { saveOrUpdateFile } from "./file-editing-utils";
-import { getFullPath } from "./file-path-utils";
-import { listFilesAndFolderInPath, readJson, readTextFromMultipleFiles } from "./file-reading-utils";
-import { searchDirForFileName } from "./file-search-utils";
-import { deletePath, directoryExists, fileExists } from "./file-validation-utils";
+import { saveOrUpdateFile } from './file-editing-utils'
+import { getFullPath } from './file-path-utils'
+import { listFilesAndFolderInPath, readJson, readTextFromMultipleFiles } from './file-reading-utils'
+import { searchDirForFileName } from './file-search-utils'
+import { deletePath, directoryExists, fileExists } from './file-validation-utils'
export function fileFactory({ baseDirectory }: { baseDirectory: string }) {
return {
@@ -11,45 +11,45 @@ export function fileFactory({ baseDirectory }: { baseDirectory: string }) {
const fullPath = await getFullPath({
baseDir: baseDirectory,
filePath: relativePath,
- });
+ })
return saveOrUpdateFile({
filePath: fullPath,
content: data,
- });
- });
- return Promise.all(promises);
+ })
+ })
+ return Promise.all(promises)
},
readTextFromMultipleFiles: (relativePaths: string[]) => {
const fullPaths = relativePaths.map((relativePath) =>
getFullPath({ baseDir: baseDirectory, filePath: relativePath }),
- );
- return readTextFromMultipleFiles(fullPaths);
+ )
+ return readTextFromMultipleFiles(fullPaths)
},
searchDirForFile: (fileName: string, relativePath?: string) => {
- let fullPath = baseDirectory;
+ let fullPath = baseDirectory
if (relativePath) {
fullPath = getFullPath({
baseDir: baseDirectory,
filePath: relativePath,
- });
+ })
}
- return searchDirForFileName(fullPath, fileName);
+ return searchDirForFileName(fullPath, fileName)
},
fileExists: (relativePath: string) => {
const fullPath = getFullPath({
baseDir: baseDirectory,
filePath: relativePath,
- });
+ })
- return fileExists(fullPath);
+ return fileExists(fullPath)
},
deleteFile: (relativePath: string) => {
- return deletePath(getFullPath({ baseDir: baseDirectory, filePath: relativePath }));
+ return deletePath(getFullPath({ baseDir: baseDirectory, filePath: relativePath }))
},
readJson: (relativePath: string) => {
- return readJson(getFullPath({ baseDir: baseDirectory, filePath: relativePath }));
+ return readJson(getFullPath({ baseDir: baseDirectory, filePath: relativePath }))
},
saveJson: (content: object, relativePath: string) => {
return saveOrUpdateFile({
@@ -59,7 +59,7 @@ export function fileFactory({ baseDirectory }: { baseDirectory: string }) {
}),
content,
isJson: true,
- });
+ })
},
directoryExists,
createFile: (content: string, relativePath: string) => {
@@ -69,16 +69,16 @@ export function fileFactory({ baseDirectory }: { baseDirectory: string }) {
filePath: relativePath,
}),
content,
- });
+ })
},
listFilesAndFolderInPath: (dirPath: string) => {
const fullPath = getFullPath({
baseDir: baseDirectory,
filePath: dirPath,
- });
+ })
- return listFilesAndFolderInPath(fullPath);
+ return listFilesAndFolderInPath(fullPath)
},
- };
+ }
}
diff --git a/files-folders/file-path-utils.test.ts b/files-folders/file-path-utils.test.ts
index 7affbbe..865accf 100644
--- a/files-folders/file-path-utils.test.ts
+++ b/files-folders/file-path-utils.test.ts
@@ -1,11 +1,11 @@
-import { describe, expect, it } from "bun:test";
-import { getFullPath } from "./file-path-utils";
+import { describe, expect, it } from 'bun:test'
+import { getFullPath } from './file-path-utils'
-describe("getFullPath", () => {
- it("should return an absolute path when given an absolute path", () => {
- const baseDirectory = "/Users/brandon";
- const filePath = "/Users/brandon/Documents/file.txt";
- const result = getFullPath({ baseDir: baseDirectory, filePath });
- expect(result).toEqual(filePath);
- });
-});
+describe('getFullPath', () => {
+ it('should return an absolute path when given an absolute path', () => {
+ const baseDirectory = '/Users/brandon'
+ const filePath = '/Users/brandon/Documents/file.txt'
+ const result = getFullPath({ baseDir: baseDirectory, filePath })
+ expect(result).toEqual(filePath)
+ })
+})
diff --git a/files-folders/file-path-utils.ts b/files-folders/file-path-utils.ts
index 45f5060..f38b109 100644
--- a/files-folders/file-path-utils.ts
+++ b/files-folders/file-path-utils.ts
@@ -1,21 +1,21 @@
-import path from "path";
+import path from 'path'
export function getFullPath({
baseDir,
filePath,
isAbsolute = false,
}: {
- baseDir: string;
- filePath: string;
- isAbsolute?: boolean;
+ baseDir: string
+ filePath: string
+ isAbsolute?: boolean
}): string {
try {
if (isAbsolute || path.isAbsolute(filePath)) {
- return filePath;
+ return filePath
} else {
- return path.resolve(baseDir, filePath);
+ return path.resolve(baseDir, filePath)
}
} catch (e) {
- throw e;
+ throw e
}
}
diff --git a/files-folders/file-reading-utils.ts b/files-folders/file-reading-utils.ts
index cdedb49..96323bc 100644
--- a/files-folders/file-reading-utils.ts
+++ b/files-folders/file-reading-utils.ts
@@ -1,27 +1,27 @@
-import fsPromise from "fs/promises";
-import path from "path";
-import { FileDirInfo } from "./file-types";
+import fsPromise from 'fs/promises'
+import path from 'path'
+import { FileDirInfo } from './file-types'
export async function readFileContent(filePath: string): Promise {
try {
- return Bun.file(filePath).text();
+ return Bun.file(filePath).text()
} catch (error) {
- throw error;
+ throw error
}
}
export async function listFilesAndFolderInPath(fullPath: string): Promise {
const entries = await fsPromise.readdir(fullPath, {
withFileTypes: true,
- });
+ })
const filesAndFolders = entries.map((entry) => {
- const name = entry.name;
- const entryFullPath = path.join(fullPath, name);
- const bunFileInfo = Bun.file(entryFullPath);
- const type = entry.isFile() ? "file" : entry.isDirectory() ? "directory" : "other";
- let fileExtension = "folder";
- if (type === "file") {
- const regex = /(?:\.([^.]+))?$/;
- fileExtension = regex.exec(entryFullPath)?.[1] ?? "file";
+ const name = entry.name
+ const entryFullPath = path.join(fullPath, name)
+ const bunFileInfo = Bun.file(entryFullPath)
+ const type = entry.isFile() ? 'file' : entry.isDirectory() ? 'directory' : 'other'
+ let fileExtension = 'folder'
+ if (type === 'file') {
+ const regex = /(?:\.([^.]+))?$/
+ fileExtension = regex.exec(entryFullPath)?.[1] ?? 'file'
}
return {
type,
@@ -29,19 +29,19 @@ export async function listFilesAndFolderInPath(fullPath: string): Promise {
const promises = filePaths.map(async (filePath) => {
- return readFileContent(filePath);
- });
- return Promise.all(promises);
+ return readFileContent(filePath)
+ })
+ return Promise.all(promises)
}
export async function readJson(filePath: string): Promise {
- const rawText = await Bun.file(filePath).text();
- return JSON.parse(rawText);
+ const rawText = await Bun.file(filePath).text()
+ return JSON.parse(rawText)
}
diff --git a/files-folders/file-search-utils.test.ts b/files-folders/file-search-utils.test.ts
index 6bb05a6..eb810b2 100644
--- a/files-folders/file-search-utils.test.ts
+++ b/files-folders/file-search-utils.test.ts
@@ -1,36 +1,36 @@
-import { describe, expect, it } from "bun:test";
-import { saveOrUpdateFile } from "./file-editing-utils";
-import { recursiveDirSearch, searchDirForFileName } from "./file-search-utils";
-import { deletePath } from "./file-validation-utils";
+import { describe, expect, it } from 'bun:test'
+import { saveOrUpdateFile } from './file-editing-utils'
+import { recursiveDirSearch, searchDirForFileName } from './file-search-utils'
+import { deletePath } from './file-validation-utils'
-const testDir = process.env.PWD + "/files-folders/test";
-const nestedDir = testDir + "/nestedDir";
+const testDir = process.env.PWD + '/files-folders/test'
+const nestedDir = testDir + '/nestedDir'
-describe("Search Utilities", () => {
- it("should search directory and return matched files", async () => {
+describe('Search Utilities', () => {
+ it('should search directory and return matched files', async () => {
// Setup
- const testFile1 = testDir + "/test1.txt";
- const testFile2 = nestedDir + "/test2.txt";
+ const testFile1 = testDir + '/test1.txt'
+ const testFile2 = nestedDir + '/test2.txt'
- await saveOrUpdateFile({ filePath: testFile1, content: "Hello, world!" });
- await saveOrUpdateFile({ filePath: testFile2, content: "Goodbye, world!" });
+ await saveOrUpdateFile({ filePath: testFile1, content: 'Hello, world!' })
+ await saveOrUpdateFile({ filePath: testFile2, content: 'Goodbye, world!' })
// Test recursiveDirSearch
const results = await recursiveDirSearch({
directory: testDir,
- searchString: "test",
- });
+ searchString: 'test',
+ })
- expect(results.length).toEqual(2);
- expect(results.map((r) => r.fullPath)).toContain(testFile1);
- expect(results.map((r) => r.fullPath)).toContain(testFile2);
+ expect(results.length).toEqual(2)
+ expect(results.map((r) => r.fullPath)).toContain(testFile1)
+ expect(results.map((r) => r.fullPath)).toContain(testFile2)
// Test searchDirForFile
- const foundPath = await searchDirForFileName(testDir, "test2.txt");
- expect(foundPath).toEqual(testFile2);
+ const foundPath = await searchDirForFileName(testDir, 'test2.txt')
+ expect(foundPath).toEqual(testFile2)
// Cleanup
- await deletePath(testFile1);
- await deletePath(testFile2);
- });
-});
+ await deletePath(testFile1)
+ await deletePath(testFile2)
+ })
+})
diff --git a/files-folders/file-search-utils.ts b/files-folders/file-search-utils.ts
index e527f52..71f25f4 100644
--- a/files-folders/file-search-utils.ts
+++ b/files-folders/file-search-utils.ts
@@ -1,48 +1,48 @@
-import fsPromise from "fs/promises";
+import fsPromise from 'fs/promises'
-import path from "path";
-import { FileDirInfo, FileWithContent } from "./file-types";
+import path from 'path'
+import { FileDirInfo, FileWithContent } from './file-types'
export type FileSearchConfig = {
- [key: string]: boolean;
-};
+ [key: string]: boolean
+}
export const defaultDirIgnore = {
node_modules: true,
- ".git": true,
- ".vscode": true,
- ".idea": true,
+ '.git': true,
+ '.vscode': true,
+ '.idea': true,
cache: true,
-};
+}
export const defaultExtIgnore = {
log: true,
localstorage: true,
DS_Store: true,
testing: true,
-};
+}
export const readFileInfo = (filePath: string): FileDirInfo => {
- const bunFileInfo = Bun.file(filePath);
+ const bunFileInfo = Bun.file(filePath)
- const extension = path.extname(bunFileInfo?.name || "").slice(1);
+ const extension = path.extname(bunFileInfo?.name || '').slice(1)
return {
- type: "file",
- name: filePath.split("/").pop() || "",
+ type: 'file',
+ name: filePath.split('/').pop() || '',
fullPath: filePath,
size: bunFileInfo.size,
extension,
- };
-};
+ }
+}
export type FileSearchParams = {
- directory: string;
- searchString: string;
- ignoreDirectories?: object;
- ignoreFileTypes?: object;
- searchContent?: T;
-};
+ directory: string
+ searchString: string
+ ignoreDirectories?: object
+ ignoreFileTypes?: object
+ searchContent?: T
+}
export const recursiveDirSearch = async (
params: FileSearchParams,
@@ -53,68 +53,68 @@ export const recursiveDirSearch = async (
ignoreDirectories = defaultDirIgnore,
ignoreFileTypes = defaultExtIgnore,
searchContent = false,
- } = params;
+ } = params
- const results = [];
- const entries = await fsPromise.readdir(directory, { withFileTypes: true });
+ const results = []
+ const entries = await fsPromise.readdir(directory, { withFileTypes: true })
for (const entry of entries) {
- const fullPath = path.join(directory, entry.name);
+ const fullPath = path.join(directory, entry.name)
if (entry.isDirectory() && !ignoreDirectories[entry.name as keyof typeof ignoreDirectories]) {
const nestedResults = await recursiveDirSearch({
...params,
directory: fullPath,
- });
- results.push(...nestedResults);
+ })
+ results.push(...nestedResults)
} else if (entry.isFile()) {
- const extension = path.extname(entry.name).slice(1);
+ const extension = path.extname(entry.name).slice(1)
- if (ignoreFileTypes[extension as keyof typeof ignoreFileTypes]) continue;
+ if (ignoreFileTypes[extension as keyof typeof ignoreFileTypes]) continue
if (searchContent) {
- const content = await Bun.file(fullPath).text();
+ const content = await Bun.file(fullPath).text()
if (content.includes(searchString)) {
- const bunFileInfo = Bun.file(fullPath);
+ const bunFileInfo = Bun.file(fullPath)
results.push({
- type: "file",
+ type: 'file',
name: entry.name,
fullPath,
size: bunFileInfo.size,
extension,
content,
- });
+ })
}
} else if (entry.name.includes(searchString)) {
- const bunFileInfo = Bun.file(fullPath);
+ const bunFileInfo = Bun.file(fullPath)
results.push({
- type: "file",
+ type: 'file',
name: entry.name,
fullPath,
size: bunFileInfo.size,
extension,
- });
+ })
}
}
}
- return results as T extends true ? FileWithContent[] : FileDirInfo[];
-};
+ return results as T extends true ? FileWithContent[] : FileDirInfo[]
+}
export async function searchDirForFileName(startingDir: string, fileName: string): Promise {
async function searchFn(dir: string): Promise {
- const entries = await fsPromise.readdir(dir, { withFileTypes: true });
+ const entries = await fsPromise.readdir(dir, { withFileTypes: true })
for (let entry of entries) {
- const fullPath = path.join(dir, entry.name);
+ const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
if (await searchFn(fullPath)) {
- return fullPath + "/" + fileName;
+ return fullPath + '/' + fileName
}
} else if (entry.name === fileName) {
- return fullPath + "/" + fileName;
+ return fullPath + '/' + fileName
}
}
- return null;
+ return null
}
- return searchFn(startingDir);
+ return searchFn(startingDir)
}
diff --git a/files-folders/file-types.ts b/files-folders/file-types.ts
index 35b4ea3..3fc6e4a 100644
--- a/files-folders/file-types.ts
+++ b/files-folders/file-types.ts
@@ -1,12 +1,12 @@
export type FileDirInfo = {
- type: "file" | "directory" | "other";
- name: string;
- fullPath: string;
- size: number;
- extension: string;
-};
+ type: 'file' | 'directory' | 'other'
+ name: string
+ fullPath: string
+ size: number
+ extension: string
+}
export type FileWithContent = FileDirInfo & {
- content: string;
- type: "file";
-};
+ content: string
+ type: 'file'
+}
diff --git a/files-folders/file-validation-utils.test.ts b/files-folders/file-validation-utils.test.ts
index d0be3ef..501f052 100644
--- a/files-folders/file-validation-utils.test.ts
+++ b/files-folders/file-validation-utils.test.ts
@@ -1,56 +1,56 @@
-import { describe, expect, it } from "bun:test";
-import { saveOrUpdateFile } from "./file-editing-utils";
-import { getFullPath } from "./file-path-utils";
-import { deletePath, directoryExists, fileExists } from "./file-validation-utils";
-
-const savePath = process.env.PWD + "/files-folders/test";
-const saveFile = savePath + "/test.txt";
-
-describe("fileExists", () => {
- it("should return true and false", async () => {
- await saveOrUpdateFile({ filePath: saveFile, content: "test test test" });
- expect(await fileExists(saveFile)).toBe(true);
- await deletePath(saveFile);
- expect(await fileExists(saveFile)).toBe(false);
- });
-
- it("should return false if file does not exist", async () => {
- const filePath = "/path/to/fake-file.txt";
-
- const result = await fileExists(filePath);
- expect(result).toBe(false);
- });
-});
-
-describe("deleteFile", () => {
- it("should delete file", async () => {
- await saveOrUpdateFile({ filePath: saveFile, content: "test test test" });
- expect(await fileExists(saveFile)).toBe(true);
- await deletePath(saveFile);
- expect(await fileExists(saveFile)).toBe(false);
- });
-});
-
-describe("directoryExists", () => {
- it("should create directory if it does not exist", async () => {
- expect(await directoryExists({ path: savePath, createMissingDirs: true })).toBe(true);
- });
-
- it("should not create directory if it already exists", async () => {
- const directoryPath = "files-folders/fake";
+import { describe, expect, it } from 'bun:test'
+import { saveOrUpdateFile } from './file-editing-utils'
+import { getFullPath } from './file-path-utils'
+import { deletePath, directoryExists, fileExists } from './file-validation-utils'
+
+const savePath = process.env.PWD + '/files-folders/test'
+const saveFile = savePath + '/test.txt'
+
+describe('fileExists', () => {
+ it('should return true and false', async () => {
+ await saveOrUpdateFile({ filePath: saveFile, content: 'test test test' })
+ expect(await fileExists(saveFile)).toBe(true)
+ await deletePath(saveFile)
+ expect(await fileExists(saveFile)).toBe(false)
+ })
+
+ it('should return false if file does not exist', async () => {
+ const filePath = '/path/to/fake-file.txt'
+
+ const result = await fileExists(filePath)
+ expect(result).toBe(false)
+ })
+})
+
+describe('deleteFile', () => {
+ it('should delete file', async () => {
+ await saveOrUpdateFile({ filePath: saveFile, content: 'test test test' })
+ expect(await fileExists(saveFile)).toBe(true)
+ await deletePath(saveFile)
+ expect(await fileExists(saveFile)).toBe(false)
+ })
+})
+
+describe('directoryExists', () => {
+ it('should create directory if it does not exist', async () => {
+ expect(await directoryExists({ path: savePath, createMissingDirs: true })).toBe(true)
+ })
+
+ it('should not create directory if it already exists', async () => {
+ const directoryPath = 'files-folders/fake'
const fullPath = getFullPath({
- baseDir: ".",
+ baseDir: '.',
filePath: directoryPath,
- });
+ })
- Bun.spawnSync({ cmd: ["rm", "-rf", directoryPath] });
+ Bun.spawnSync({ cmd: ['rm', '-rf', directoryPath] })
- expect(await directoryExists({ path: fullPath })).toBe(false);
+ expect(await directoryExists({ path: fullPath })).toBe(false)
- await directoryExists({ path: fullPath, createMissingDirs: true });
+ await directoryExists({ path: fullPath, createMissingDirs: true })
- expect(await directoryExists({ path: fullPath })).toBe(true);
+ expect(await directoryExists({ path: fullPath })).toBe(true)
- Bun.spawnSync({ cmd: ["rm", "-rf", directoryPath] });
- });
-});
+ Bun.spawnSync({ cmd: ['rm', '-rf', directoryPath] })
+ })
+})
diff --git a/files-folders/file-validation-utils.ts b/files-folders/file-validation-utils.ts
index 5630b87..91e2813 100644
--- a/files-folders/file-validation-utils.ts
+++ b/files-folders/file-validation-utils.ts
@@ -1,62 +1,62 @@
-import fsPromise from "fs/promises";
-import path from "path";
+import fsPromise from 'fs/promises'
+import path from 'path'
export async function fileExists(filePath: string): Promise {
try {
- return await Bun.file(filePath).exists();
+ return await Bun.file(filePath).exists()
} catch (error) {
- return false;
+ return false
}
}
export async function deletePath(targetPath: string) {
- let dirData;
+ let dirData
try {
- dirData = await fsPromise.stat(targetPath);
+ dirData = await fsPromise.stat(targetPath)
} catch (error) {
- console.error(error);
+ console.error(error)
}
if (dirData && dirData.isDirectory()) {
for (const file of await fsPromise.readdir(targetPath)) {
- await deletePath(path.join(targetPath, file));
+ await deletePath(path.join(targetPath, file))
}
- await fsPromise.rm(targetPath);
+ await fsPromise.rm(targetPath)
} else if (dirData && dirData.isFile()) {
// delete file
- await fsPromise.unlink(targetPath);
+ await fsPromise.unlink(targetPath)
} else {
- console.info(`deletePath: Path does not exist: ${targetPath}`);
+ console.info(`deletePath: Path does not exist: ${targetPath}`)
}
}
export async function directoryExists({
createMissingDirs = false,
path,
}: {
- path: string;
- createMissingDirs?: boolean;
+ path: string
+ createMissingDirs?: boolean
}): Promise {
try {
- const stat = await fsPromise.stat(path);
- return stat.isDirectory();
+ const stat = await fsPromise.stat(path)
+ return stat.isDirectory()
} catch (error: any) {
- if (error?.code === "ENOENT") {
- if (!createMissingDirs && typeof createMissingDirs !== "boolean") {
+ if (error?.code === 'ENOENT') {
+ if (!createMissingDirs && typeof createMissingDirs !== 'boolean') {
console.error(
`directoryExists: Directory does not exist: ${path}, but createMissingDirs is false, set to true to create the directory.`,
- );
- return false;
+ )
+ return false
}
if (createMissingDirs) {
- await fsPromise.mkdir(path, { recursive: true });
- console.info(`directoryExists: Created directory: ${path}`);
- return true;
+ await fsPromise.mkdir(path, { recursive: true })
+ console.info(`directoryExists: Created directory: ${path}`)
+ return true
}
- return false;
+ return false
} else {
- throw error;
+ throw error
}
}
}
diff --git a/files-folders/files-folder.test.ts b/files-folders/files-folder.test.ts
index f764009..da4de1b 100644
--- a/files-folders/files-folder.test.ts
+++ b/files-folders/files-folder.test.ts
@@ -1,199 +1,199 @@
-import fsPromise from "fs/promises";
+import fsPromise from 'fs/promises'
-import { afterEach, describe, expect, it, jest, spyOn } from "bun:test";
-import path from "path";
-import { saveOrUpdateFile } from "./file-editing-utils";
-import { fileFactory } from "./file-factory";
+import { afterEach, describe, expect, it, jest, spyOn } from 'bun:test'
+import path from 'path'
+import { saveOrUpdateFile } from './file-editing-utils'
+import { fileFactory } from './file-factory'
const getTestFactory = () => {
return fileFactory({
- baseDirectory: ".",
- });
-};
+ baseDirectory: '.',
+ })
+}
// Tests that saveResultToFile saves the given content to a file at the specified file path
-it("saves content to file", async () => {
- const filePath = "./test/test-utils/test.txt";
- const content = "Hello, world!";
- await saveOrUpdateFile({ filePath, content });
- const fileContent = await fsPromise.readFile(filePath, "utf-8");
- expect(fileContent).toEqual(content);
+it('saves content to file', async () => {
+ const filePath = './test/test-utils/test.txt'
+ const content = 'Hello, world!'
+ await saveOrUpdateFile({ filePath, content })
+ const fileContent = await fsPromise.readFile(filePath, 'utf-8')
+ expect(fileContent).toEqual(content)
- const factory = getTestFactory();
+ const factory = getTestFactory()
// delete file
- await factory.deleteFile(filePath);
+ await factory.deleteFile(filePath)
// expect file to no longer be there
try {
- await fsPromise.access(filePath, fsPromise.constants.F_OK);
- throw new Error("File should not exist");
+ await fsPromise.access(filePath, fsPromise.constants.F_OK)
+ throw new Error('File should not exist')
} catch (e: any) {
- if (e.code === "ENOENT") {
+ if (e.code === 'ENOENT') {
// This is expected as the file should not exist
} else {
// If the error is something else, re-throw it
- throw e;
+ throw e
}
}
-});
+})
-describe("fileFactory", async () => {
+describe('fileFactory', async () => {
afterEach(() => {
- jest.restoreAllMocks();
- });
+ jest.restoreAllMocks()
+ })
// Test that readFilesRawText reads the content of multiple files correctly
- it("reads multiple files", async () => {
- const factory = getTestFactory();
+ it('reads multiple files', async () => {
+ const factory = getTestFactory()
- const filePaths = ["./files-folders/test/test1.txt", "./files-folders/test/test2.txt"];
+ const filePaths = ['./files-folders/test/test1.txt', './files-folders/test/test2.txt']
- const contents = ["Hello, world!", "Goodbye, world!"];
+ const contents = ['Hello, world!', 'Goodbye, world!']
for (let i = 0; i < filePaths.length; i++) {
- await factory.createFile(contents[i], filePaths[i]);
+ await factory.createFile(contents[i], filePaths[i])
}
- const fileContents = await factory.readTextFromMultipleFiles(filePaths);
- expect(fileContents).toEqual(contents);
+ const fileContents = await factory.readTextFromMultipleFiles(filePaths)
+ expect(fileContents).toEqual(contents)
for (let filePath of filePaths) {
- await factory.deleteFile(filePath);
+ await factory.deleteFile(filePath)
}
- });
+ })
// Test that updateFiles updates the content of multiple files correctly
- it("updates multiple files", async () => {
- const factory = getTestFactory();
- const filePaths = ["./test/test-utils/test1.txt", "./test/test-utils/test2.txt"];
- const initialContents = ["Hello, world!", "Goodbye, world!"];
- const newContents = "Updated content";
+ it('updates multiple files', async () => {
+ const factory = getTestFactory()
+ const filePaths = ['./test/test-utils/test1.txt', './test/test-utils/test2.txt']
+ const initialContents = ['Hello, world!', 'Goodbye, world!']
+ const newContents = 'Updated content'
for (let i = 0; i < filePaths.length; i++) {
await saveOrUpdateFile({
filePath: filePaths[i],
content: initialContents[i],
- });
+ })
}
- await factory.updateFiles(filePaths, newContents);
+ await factory.updateFiles(filePaths, newContents)
for (let filePath of filePaths) {
- const fileContent = await fsPromise.readFile(filePath, "utf-8");
- expect(fileContent).toEqual(newContents);
- await factory.deleteFile(filePath);
+ const fileContent = await fsPromise.readFile(filePath, 'utf-8')
+ expect(fileContent).toEqual(newContents)
+ await factory.deleteFile(filePath)
}
- });
+ })
// Test that searchDirectory can correctly find a file in the specified directory
- it("recursively searches directory for file", async () => {
- const factory = getTestFactory();
- const fileName = "test.txt";
- const filePath = `./test/test-utils/${fileName}`;
- const content = "Hello, world!";
- await saveOrUpdateFile({ filePath, content });
- const fileExists = await factory.searchDirForFile(fileName);
- expect(fileExists).toEqual("test/test.txt");
- await factory.deleteFile(filePath);
- });
+ it('recursively searches directory for file', async () => {
+ const factory = getTestFactory()
+ const fileName = 'test.txt'
+ const filePath = `./test/test-utils/${fileName}`
+ const content = 'Hello, world!'
+ await saveOrUpdateFile({ filePath, content })
+ const fileExists = await factory.searchDirForFile(fileName)
+ expect(fileExists).toEqual('test/test.txt')
+ await factory.deleteFile(filePath)
+ })
// Test that fileExists correctly identifies if a file exists at the given path
- it("checks if file exists", async () => {
- const factory = getTestFactory();
- const fileName = "test.txt";
- const filePath = `./test/test-utils/${fileName}`;
- const content = "Hello, world!";
- await saveOrUpdateFile({ filePath, content });
- const fileExists = await factory.fileExists(filePath);
- expect(fileExists).toEqual(true);
- await factory.deleteFile(filePath);
- });
+ it('checks if file exists', async () => {
+ const factory = getTestFactory()
+ const fileName = 'test.txt'
+ const filePath = `./test/test-utils/${fileName}`
+ const content = 'Hello, world!'
+ await saveOrUpdateFile({ filePath, content })
+ const fileExists = await factory.fileExists(filePath)
+ expect(fileExists).toEqual(true)
+ await factory.deleteFile(filePath)
+ })
// Test that deleteFile deletes a file correctly
- it("deletes a file", async () => {
- const factory = getTestFactory();
- const fileName = "test.txt";
- const filePath = `./test/test-utils/${fileName}`;
- const content = "Hello, world!";
- await saveOrUpdateFile({ filePath, content });
- await factory.deleteFile(filePath);
- const fileExistsAfterDeletion = await factory.fileExists(filePath);
- expect(fileExistsAfterDeletion).toEqual(false);
- });
+ it('deletes a file', async () => {
+ const factory = getTestFactory()
+ const fileName = 'test.txt'
+ const filePath = `./test/test-utils/${fileName}`
+ const content = 'Hello, world!'
+ await saveOrUpdateFile({ filePath, content })
+ await factory.deleteFile(filePath)
+ const fileExistsAfterDeletion = await factory.fileExists(filePath)
+ expect(fileExistsAfterDeletion).toEqual(false)
+ })
// Test that readJson reads a JSON file correctly
- it("reads JSON file", async () => {
- const factory = getTestFactory();
- const fileName = "test.json";
- const filePath = `./test/test-utils/${fileName}`;
- const jsonContent = { greeting: "Hello, world!" };
- await fsPromise.writeFile(filePath, JSON.stringify(jsonContent));
- const content = await factory.readJson(filePath);
- expect(content).toEqual(jsonContent);
- await factory.deleteFile(filePath);
- });
+ it('reads JSON file', async () => {
+ const factory = getTestFactory()
+ const fileName = 'test.json'
+ const filePath = `./test/test-utils/${fileName}`
+ const jsonContent = { greeting: 'Hello, world!' }
+ await fsPromise.writeFile(filePath, JSON.stringify(jsonContent))
+ const content = await factory.readJson(filePath)
+ expect(content).toEqual(jsonContent)
+ await factory.deleteFile(filePath)
+ })
// Test that writeJson writes a JSON object to a file correctly
- it("writes JSON file", async () => {
- const factory = getTestFactory();
- const fileName = "test.json";
- const filePath = `./${fileName}`;
+ it('writes JSON file', async () => {
+ const factory = getTestFactory()
+ const fileName = 'test.json'
+ const filePath = `./${fileName}`
- const jsonContent = { farewell: "Goodbye, world!" };
+ const jsonContent = { farewell: 'Goodbye, world!' }
try {
- await factory.saveJson(jsonContent, filePath);
+ await factory.saveJson(jsonContent, filePath)
} catch (error) {
- console.error(error);
+ console.error(error)
}
- let fileContent = "";
+ let fileContent = ''
try {
- fileContent = JSON.parse(await fsPromise.readFile(filePath, "utf-8"));
+ fileContent = JSON.parse(await fsPromise.readFile(filePath, 'utf-8'))
} catch (e) {
- console.error(e);
+ console.error(e)
}
- expect(fileContent).toEqual(jsonContent);
+ expect(fileContent).toEqual(jsonContent)
try {
- await factory.deleteFile(filePath);
+ await factory.deleteFile(filePath)
} catch (error) {
- console.error(error);
+ console.error(error)
}
- });
+ })
- it("should list files and directories", async () => {
- const readdirSpy = spyOn(fsPromise, "readdir");
+ it('should list files and directories', async () => {
+ const readdirSpy = spyOn(fsPromise, 'readdir')
//@ts-ignore
readdirSpy.mockResolvedValue([
{
isFile: () => true,
isDirectory: () => false,
- name: "some-file.txt",
+ name: 'some-file.txt',
},
{
isFile: () => false,
isDirectory: () => true,
- name: "some-dir",
+ name: 'some-dir',
},
- ]);
+ ])
- const factory = fileFactory({ baseDirectory: "path/to" });
+ const factory = fileFactory({ baseDirectory: 'path/to' })
- const result = await factory.listFilesAndFolderInPath("");
+ const result = await factory.listFilesAndFolderInPath('')
expect(result).toEqual([
{
- extension: "txt",
- type: "file",
- name: "some-file.txt",
- fullPath: path.resolve("path/to", "some-file.txt"),
+ extension: 'txt',
+ type: 'file',
+ name: 'some-file.txt',
+ fullPath: path.resolve('path/to', 'some-file.txt'),
size: 0,
},
{
- type: "directory",
- extension: "folder",
- name: "some-dir",
- fullPath: path.resolve("path/to", "some-dir"),
+ type: 'directory',
+ extension: 'folder',
+ name: 'some-dir',
+ fullPath: path.resolve('path/to', 'some-dir'),
size: 0,
},
- ]);
- });
-});
+ ])
+ })
+})
diff --git a/files-folders/index.ts b/files-folders/index.ts
index 2299774..a8ceb86 100644
--- a/files-folders/index.ts
+++ b/files-folders/index.ts
@@ -1,19 +1,14 @@
-export { saveOrUpdateFile, updateMultipleFiles } from "./file-editing-utils";
-export { fileExtensionMap, fullMimeForExtension } from "./file-extension-map";
-export type { ExtensionMapKeys, FileExtensionType } from "./file-extension-map";
-export { fileFactory } from "./file-factory";
-export { getFullPath } from "./file-path-utils";
-export {
- listFilesAndFolderInPath,
- readFileContent,
- readJson,
- readTextFromMultipleFiles,
-} from "./file-reading-utils";
+export { saveOrUpdateFile, updateMultipleFiles } from './file-editing-utils'
+export { fileExtensionMap, fullMimeForExtension } from './file-extension-map'
+export type { ExtensionMapKeys, FileExtensionType } from './file-extension-map'
+export { fileFactory } from './file-factory'
+export { getFullPath } from './file-path-utils'
+export { listFilesAndFolderInPath, readFileContent, readJson, readTextFromMultipleFiles } from './file-reading-utils'
export {
defaultDirIgnore,
defaultExtIgnore,
readFileInfo,
recursiveDirSearch,
searchDirForFileName,
-} from "./file-search-utils";
-export type { FileSearchConfig, FileSearchParams } from "./file-search-utils";
+} from './file-search-utils'
+export type { FileSearchConfig, FileSearchParams } from './file-search-utils'
diff --git a/htmlody/constants.ts b/htmlody/constants.ts
index 22448c1..303315a 100644
--- a/htmlody/constants.ts
+++ b/htmlody/constants.ts
@@ -1,116 +1,116 @@
export const SELF_CLOSING_TAGS = new Set([
- "area",
- "base",
- "br",
- "col",
- "command",
- "embed",
- "hr",
- "img",
- "input",
- "keygen",
- "link",
- "meta",
- "param",
- "source",
- "track",
- "wbr",
-]);
+ 'area',
+ 'base',
+ 'br',
+ 'col',
+ 'command',
+ 'embed',
+ 'hr',
+ 'img',
+ 'input',
+ 'keygen',
+ 'link',
+ 'meta',
+ 'param',
+ 'source',
+ 'track',
+ 'wbr',
+])
export const htmlTags = [
// Metadata and scripting
- "base",
- "head",
- "link",
- "meta",
- "noscript",
- "script",
- "style",
- "title",
+ 'base',
+ 'head',
+ 'link',
+ 'meta',
+ 'noscript',
+ 'script',
+ 'style',
+ 'title',
// Sections
- "body",
- "footer",
- "header",
- "h1",
- "h2",
- "h3",
- "h4",
- "h5",
- "h6",
- "nav",
- "section",
+ 'body',
+ 'footer',
+ 'header',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'nav',
+ 'section',
// Grouping content
- "blockquote",
- "div",
- "figure",
- "hr",
- "li",
- "main",
- "ol",
- "p",
- "pre",
- "ul",
+ 'blockquote',
+ 'div',
+ 'figure',
+ 'hr',
+ 'li',
+ 'main',
+ 'ol',
+ 'p',
+ 'pre',
+ 'ul',
// Text-level semantics
- "a",
- "abbr",
- "b",
- "br",
- "cite",
- "code",
- "data",
- "em",
- "i",
- "span",
- "strong",
- "time",
- "u",
+ 'a',
+ 'abbr',
+ 'b',
+ 'br',
+ 'cite',
+ 'code',
+ 'data',
+ 'em',
+ 'i',
+ 'span',
+ 'strong',
+ 'time',
+ 'u',
// Forms
- "button",
- "datalist",
- "fieldset",
- "form",
- "input",
- "label",
- "legend",
- "meter",
- "optgroup",
- "option",
- "output",
- "progress",
- "select",
- "textarea",
+ 'button',
+ 'datalist',
+ 'fieldset',
+ 'form',
+ 'input',
+ 'label',
+ 'legend',
+ 'meter',
+ 'optgroup',
+ 'option',
+ 'output',
+ 'progress',
+ 'select',
+ 'textarea',
// Embedded content
- "audio",
- "canvas",
- "embed",
- "iframe",
- "img",
- "map",
- "object",
- "picture",
- "source",
- "track",
- "video",
+ 'audio',
+ 'canvas',
+ 'embed',
+ 'iframe',
+ 'img',
+ 'map',
+ 'object',
+ 'picture',
+ 'source',
+ 'track',
+ 'video',
// Tables
- "caption",
- "col",
- "colgroup",
- "table",
- "tbody",
- "td",
- "tfoot",
- "th",
- "thead",
- "tr",
- "turbo-frame",
-] as const;
+ 'caption',
+ 'col',
+ 'colgroup',
+ 'table',
+ 'tbody',
+ 'td',
+ 'tfoot',
+ 'th',
+ 'thead',
+ 'tr',
+ 'turbo-frame',
+] as const
-export type HtmlTags = (typeof htmlTags)[number];
+export type HtmlTags = (typeof htmlTags)[number]
-export const htmlTagsSet = new Set(htmlTags);
+export const htmlTagsSet = new Set(htmlTags)
diff --git a/htmlody/css-engine.test.ts b/htmlody/css-engine.test.ts
index 16e38f2..458f9af 100644
--- a/htmlody/css-engine.test.ts
+++ b/htmlody/css-engine.test.ts
@@ -1,5 +1,5 @@
-import { describe, expect, it } from "bun:test";
-import { JsonHtmlNodeTree, JsonTagElNode } from ".";
+import { describe, expect, it } from 'bun:test'
+import { JsonHtmlNodeTree, JsonTagElNode } from '.'
import {
adjustBrightness,
generateCSS,
@@ -8,208 +8,208 @@ import {
generateVariablesForColor,
hexToRgb,
rgbToHex,
-} from "./css-engine"; // Update this import path
-import { ClassRecordAttributes } from "./htmlody-plugins";
+} from './css-engine' // Update this import path
+import { ClassRecordAttributes } from './htmlody-plugins'
-describe("generateCSS", () => {
- it("should generate correct CSS from nodeMap", () => {
+describe('generateCSS', () => {
+ it('should generate correct CSS from nodeMap', () => {
const mockNodeMap: JsonHtmlNodeTree> = {
exampleDiv: {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
+ '*': {
flex: true,
- "m-1": true,
+ 'm-1': true,
grid: false,
- "w-1/2": true,
+ 'w-1/2': true,
},
},
},
exampleSpan: {
- tag: "span",
+ tag: 'span',
cr: {
- "*": {
- "h-1/2": true,
- "p-1": true,
+ '*': {
+ 'h-1/2': true,
+ 'p-1': true,
flex: false,
},
},
},
- };
+ }
- const result = generateCSS(mockNodeMap);
+ const result = generateCSS(mockNodeMap)
const expectedCss =
`.flex { display: flex; }\n` +
`.m-1 { margin: 0.25rem; }\n` +
`.w-1/2 { width: 50%; }\n` +
`.h-1/2 { height: 50%; }\n` +
- `.p-1 { padding: 0.25rem; }\n`;
+ `.p-1 { padding: 0.25rem; }\n`
- expect(result).toEqual(expectedCss);
- });
- it("should return empty string for empty nodeMap", () => {
- const mockNodeMap: JsonHtmlNodeTree> = {};
+ expect(result).toEqual(expectedCss)
+ })
+ it('should return empty string for empty nodeMap', () => {
+ const mockNodeMap: JsonHtmlNodeTree> = {}
- const result = generateCSS(mockNodeMap);
- expect(result).toEqual(null);
- });
+ const result = generateCSS(mockNodeMap)
+ expect(result).toEqual(null)
+ })
- it("should handle nodes without the cr property", () => {
+ it('should handle nodes without the cr property', () => {
const mockNodeMap: JsonHtmlNodeTree> = {
exampleDiv: {
- tag: "div",
+ tag: 'div',
},
- };
+ }
- const result = generateCSS(mockNodeMap);
- expect(result).toEqual(null);
- });
+ const result = generateCSS(mockNodeMap)
+ expect(result).toEqual(null)
+ })
- it("should ignore invalid class names", () => {
+ it('should ignore invalid class names', () => {
const mockNodeMap: JsonHtmlNodeTree> = {
exampleDiv: {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
- "invalid-class": true,
+ '*': {
+ 'invalid-class': true,
},
},
},
- };
+ }
- const result = generateCSS(mockNodeMap);
- expect(result).toEqual(null);
- });
+ const result = generateCSS(mockNodeMap)
+ expect(result).toEqual(null)
+ })
- it("should not generate CSS if all classes are set to false", () => {
+ it('should not generate CSS if all classes are set to false', () => {
const mockNodeMap: JsonHtmlNodeTree> = {
exampleDiv: {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
+ '*': {
flex: false,
- "m-1": false,
+ 'm-1': false,
},
},
},
- };
-
- const result = generateCSS(mockNodeMap);
- expect(result).toEqual(null); // No CSS generated
- });
-});
-
-import { bgColorGen, borderColorGen, textColorGen } from "./css-engine";
-
-describe("textColorGen", () => {
- it("should generate a CSS property value for text color", () => {
- const result = textColorGen("red", 500);
- expect(result["text-red-500"]).toBe("color: var(--red-500);");
- });
-
- it("should generate a CSS property value for text color with a different shade", () => {
- const result = textColorGen("red", 600);
- expect(result["text-red-600"]).toBe("color: var(--red-600);");
- });
-});
-
-describe("bgColorGen", () => {
- it("should generate a CSS property value for background color", () => {
- const result = bgColorGen("red", 500);
- expect(result["bg-red-500"]).toBe("background-color: var(--red-500);");
- });
-});
-
-describe("borderColorGen", () => {
- it("should generate a CSS property value for border color", () => {
- const result = borderColorGen("red", 500);
- expect(result["border-red-500"]).toBe("border-color: var(--red-500);");
- });
-
- it("should generate a CSS property value for border color with a different shade", () => {
- const result = borderColorGen("red", 600);
- expect(result["border-red-600"]).toBe("border-color: var(--red-600);");
- });
-});
-
-describe("generateColorVariables", () => {
- it("should generate CSS variables for colors", () => {
- const result = generateColorVariables();
-
- expect(result).toContain(`--red-50`);
-
- expect(result).toContain(":root {\n--red-50: #1A0000;");
- expect(result).toContain("--slate-300: #4E5A65;");
- });
-});
-
-describe("generateShades", () => {
- it("should generate shades of a color", () => {
- const result = generateShades("red");
- expect(result).toHaveLength(10);
+ }
+
+ const result = generateCSS(mockNodeMap)
+ expect(result).toEqual(null) // No CSS generated
+ })
+})
+
+import { bgColorGen, borderColorGen, textColorGen } from './css-engine'
+
+describe('textColorGen', () => {
+ it('should generate a CSS property value for text color', () => {
+ const result = textColorGen('red', 500)
+ expect(result['text-red-500']).toBe('color: var(--red-500);')
+ })
+
+ it('should generate a CSS property value for text color with a different shade', () => {
+ const result = textColorGen('red', 600)
+ expect(result['text-red-600']).toBe('color: var(--red-600);')
+ })
+})
+
+describe('bgColorGen', () => {
+ it('should generate a CSS property value for background color', () => {
+ const result = bgColorGen('red', 500)
+ expect(result['bg-red-500']).toBe('background-color: var(--red-500);')
+ })
+})
+
+describe('borderColorGen', () => {
+ it('should generate a CSS property value for border color', () => {
+ const result = borderColorGen('red', 500)
+ expect(result['border-red-500']).toBe('border-color: var(--red-500);')
+ })
+
+ it('should generate a CSS property value for border color with a different shade', () => {
+ const result = borderColorGen('red', 600)
+ expect(result['border-red-600']).toBe('border-color: var(--red-600);')
+ })
+})
+
+describe('generateColorVariables', () => {
+ it('should generate CSS variables for colors', () => {
+ const result = generateColorVariables()
+
+ expect(result).toContain(`--red-50`)
+
+ expect(result).toContain(':root {\n--red-50: #1A0000;')
+ expect(result).toContain('--slate-300: #4E5A65;')
+ })
+})
+
+describe('generateShades', () => {
+ it('should generate shades of a color', () => {
+ const result = generateShades('red')
+ expect(result).toHaveLength(10)
result.forEach((shade) => {
// validate that each shade is a valid hex color
- expect(shade).toHaveLength(7);
- });
+ expect(shade).toHaveLength(7)
+ })
- expect(result[0][0]).toBe("#"); // first shade, first charact
- });
-});
+ expect(result[0][0]).toBe('#') // first shade, first charact
+ })
+})
-describe("generateVariablesForColor", () => {
- it("should generate CSS variables for shades of a color", () => {
- const result = generateVariablesForColor("red");
+describe('generateVariablesForColor', () => {
+ it('should generate CSS variables for shades of a color', () => {
+ const result = generateVariablesForColor('red')
expect(result).toBe(
- "--red-50: #1A0000;\n--red-100: #4D0000;\n--red-200: #800000;\n--red-300: #B30000;\n--red-400: #E60000;\n--red-500: #FF0000;\n--red-600: #FF1A1A;\n--red-700: #FF4D4D;\n--red-800: #FF8080;\n--red-900: #FFB3B3;\n",
- );
- });
+ '--red-50: #1A0000;\n--red-100: #4D0000;\n--red-200: #800000;\n--red-300: #B30000;\n--red-400: #E60000;\n--red-500: #FF0000;\n--red-600: #FF1A1A;\n--red-700: #FF4D4D;\n--red-800: #FF8080;\n--red-900: #FFB3B3;\n',
+ )
+ })
- it("should generate CSS variables for shades of a different color", () => {
- const result = generateVariablesForColor("blue");
+ it('should generate CSS variables for shades of a different color', () => {
+ const result = generateVariablesForColor('blue')
expect(result).toBe(
- "--blue-50: #00001A;\n--blue-100: #00004D;\n--blue-200: #000080;\n--blue-300: #0000B3;\n--blue-400: #0000E6;\n--blue-500: #0000FF;\n--blue-600: #1A1AFF;\n--blue-700: #4D4DFF;\n--blue-800: #8080FF;\n--blue-900: #B3B3FF;\n",
- );
- });
-});
-
-describe("hexToRgb", () => {
- it("should convert a hex color to an RGB object", () => {
- const result = hexToRgb("#ff0000");
- expect(result).toEqual({ r: 255, g: 0, b: 0 });
- });
-
- it("should return null for an invalid hex color", () => {
- const result = hexToRgb("invalid");
- expect(result).toBeNull();
- });
-});
-
-describe("rgbToHex", () => {
- it("should convert an RGB color to a hex string", () => {
- const result = rgbToHex(255, 0, 0);
- expect(result).toBe("#FF0000");
- });
-
- it("should handle zero values correctly", () => {
- const result = rgbToHex(0, 0, 0);
- expect(result).toBe("#000000");
- });
-});
-
-describe("adjustBrightness", () => {
- it("should adjust the brightness of an RGB color", () => {
- const result = adjustBrightness({ r: 255, g: 0, b: 0 }, 1.5);
- expect(result).toEqual({ r: 255, g: 0, b: 0 });
- });
-
- it("should not exceed the maximum brightness value", () => {
- const result = adjustBrightness({ r: 255, g: 255, b: 255 }, 10);
- expect(result).toEqual({ r: 255, g: 255, b: 255 });
- });
-
- it("should not go below the minimum brightness value", () => {
- const result = adjustBrightness({ r: 0, g: 0, b: 0 }, 0.1);
- expect(result).toEqual({ r: 0, g: 0, b: 0 });
- });
-});
+ '--blue-50: #00001A;\n--blue-100: #00004D;\n--blue-200: #000080;\n--blue-300: #0000B3;\n--blue-400: #0000E6;\n--blue-500: #0000FF;\n--blue-600: #1A1AFF;\n--blue-700: #4D4DFF;\n--blue-800: #8080FF;\n--blue-900: #B3B3FF;\n',
+ )
+ })
+})
+
+describe('hexToRgb', () => {
+ it('should convert a hex color to an RGB object', () => {
+ const result = hexToRgb('#ff0000')
+ expect(result).toEqual({ r: 255, g: 0, b: 0 })
+ })
+
+ it('should return null for an invalid hex color', () => {
+ const result = hexToRgb('invalid')
+ expect(result).toBeNull()
+ })
+})
+
+describe('rgbToHex', () => {
+ it('should convert an RGB color to a hex string', () => {
+ const result = rgbToHex(255, 0, 0)
+ expect(result).toBe('#FF0000')
+ })
+
+ it('should handle zero values correctly', () => {
+ const result = rgbToHex(0, 0, 0)
+ expect(result).toBe('#000000')
+ })
+})
+
+describe('adjustBrightness', () => {
+ it('should adjust the brightness of an RGB color', () => {
+ const result = adjustBrightness({ r: 255, g: 0, b: 0 }, 1.5)
+ expect(result).toEqual({ r: 255, g: 0, b: 0 })
+ })
+
+ it('should not exceed the maximum brightness value', () => {
+ const result = adjustBrightness({ r: 255, g: 255, b: 255 }, 10)
+ expect(result).toEqual({ r: 255, g: 255, b: 255 })
+ })
+
+ it('should not go below the minimum brightness value', () => {
+ const result = adjustBrightness({ r: 0, g: 0, b: 0 }, 0.1)
+ expect(result).toEqual({ r: 0, g: 0, b: 0 })
+ })
+})
diff --git a/htmlody/css-engine.ts b/htmlody/css-engine.ts
index 2124bb9..83ee180 100644
--- a/htmlody/css-engine.ts
+++ b/htmlody/css-engine.ts
@@ -1,130 +1,130 @@
-import { ClassRecordAttributes } from "./htmlody-plugins";
-import { ClassRecord, JsonHtmlNodeTree, JsonTagElNode, ResponsiveClassRecord } from "./htmlody-types";
+import { ClassRecordAttributes } from './htmlody-plugins'
+import { ClassRecord, JsonHtmlNodeTree, JsonTagElNode, ResponsiveClassRecord } from './htmlody-types'
const fractionPercentMap = {
- "1/2": 50,
- "1/3": 33.333333,
- "2/3": 66.666667,
- "1/4": 25,
- "2/4": 50,
- "3/4": 75,
- "1/5": 20,
- "2/5": 40,
- "3/5": 60,
- "4/5": 80,
- "1/6": 16.666667,
- "2/6": 33.333333,
- "3/6": 50,
- "4/6": 66.666667,
- "5/6": 83.333333,
- "1/12": 8.333333,
- "2/12": 16.666667,
- "3/12": 25,
- "4/12": 33.333333,
- "5/12": 41.666667,
- "6/12": 50,
- "7/12": 58.333333,
- "8/12": 66.666667,
- "9/12": 75,
- "10/12": 83.333333,
- "11/12": 91.666667,
-} as const;
+ '1/2': 50,
+ '1/3': 33.333333,
+ '2/3': 66.666667,
+ '1/4': 25,
+ '2/4': 50,
+ '3/4': 75,
+ '1/5': 20,
+ '2/5': 40,
+ '3/5': 60,
+ '4/5': 80,
+ '1/6': 16.666667,
+ '2/6': 33.333333,
+ '3/6': 50,
+ '4/6': 66.666667,
+ '5/6': 83.333333,
+ '1/12': 8.333333,
+ '2/12': 16.666667,
+ '3/12': 25,
+ '4/12': 33.333333,
+ '5/12': 41.666667,
+ '6/12': 50,
+ '7/12': 58.333333,
+ '8/12': 66.666667,
+ '9/12': 75,
+ '10/12': 83.333333,
+ '11/12': 91.666667,
+} as const
export const breakpoints = {
- sm: "640px",
- md: "768px",
- lg: "1024px",
- xl: "1280px",
-} as const;
+ sm: '640px',
+ md: '768px',
+ lg: '1024px',
+ xl: '1280px',
+} as const
-type FractionPercentMapT = typeof fractionPercentMap;
+type FractionPercentMapT = typeof fractionPercentMap
-export type CSSUnits = "rem" | "px" | "%" | "em";
+export type CSSUnits = 'rem' | 'px' | '%' | 'em'
export const cc = (keys: Keys) => {
- const keyString = typeof keys === "string" ? keys : keys.join(" ");
+ const keyString = typeof keys === 'string' ? keys : keys.join(' ')
const composition = {
- "*": {
+ '*': {
[keyString]: true,
},
- };
+ }
- return composition;
-};
+ return composition
+}
export const uClass = (keys: CSSMapKeys[]) => {
// type casted so the resulting string doesn't throw a type error,
// and keys are validated on the input
- return keys.join(" ") as "u-class";
-};
+ return keys.join(' ') as 'u-class'
+}
-export const textAlign = (val: Val) => `text-align: ${val};` as const;
-export const fontSize = (val: Val) => `font-size: ${val};` as const;
-export const textColor = (val: Val) => `color: ${val};` as const;
-export const bgColor = (val: Val) => `background-color: ${val};` as const;
+export const textAlign = (val: Val) => `text-align: ${val};` as const
+export const fontSize = (val: Val) => `font-size: ${val};` as const
+export const textColor = (val: Val) => `color: ${val};` as const
+export const bgColor = (val: Val) => `background-color: ${val};` as const
export const border = (
width: string,
style: string,
color: string,
-) => `border: ${width} ${style} ${color};` as const;
+) => `border: ${width} ${style} ${color};` as const
function extractClassNames(classRecord: ClassRecord): string[] {
return Object.entries(classRecord)
.filter(([_key, value]) => value)
- .flatMap(([key]) => key.split(" "));
+ .flatMap(([key]) => key.split(' '))
}
function generateCssSelector(breakpoint: string, className: string): string {
- const fullClassName = breakpoint === "*" ? className : `${breakpoint}_${className}`;
- const cssRule = CSS_MAP[className];
+ const fullClassName = breakpoint === '*' ? className : `${breakpoint}_${className}`
+ const cssRule = CSS_MAP[className]
- if (breakpoint === "*") {
- return `.${fullClassName} { ${cssRule} }`;
+ if (breakpoint === '*') {
+ return `.${fullClassName} { ${cssRule} }`
}
- return `@media (min-width: ${breakpoints[breakpoint]}) { .${fullClassName} { ${cssRule} } }`;
+ return `@media (min-width: ${breakpoints[breakpoint]}) { .${fullClassName} { ${cssRule} } }`
}
function processClassRecords(classRecords: ResponsiveClassRecord, usedClasses: Set): string | null {
- let cssStr = "";
+ let cssStr = ''
Object.entries(classRecords).forEach(([breakpoint, classRecord]) => {
- const classNames = extractClassNames(classRecord);
+ const classNames = extractClassNames(classRecord)
classNames.forEach((className) => {
- const fullClassName = breakpoint === "*" ? className : `${breakpoint}_${className}`;
+ const fullClassName = breakpoint === '*' ? className : `${breakpoint}_${className}`
if (!usedClasses.has(fullClassName)) {
- usedClasses.add(fullClassName);
+ usedClasses.add(fullClassName)
- if (typeof CSS_MAP[className] === "string") {
- const selector = generateCssSelector(breakpoint, className);
- cssStr += `${selector}\n`;
+ if (typeof CSS_MAP[className] === 'string') {
+ const selector = generateCssSelector(breakpoint, className)
+ cssStr += `${selector}\n`
}
}
- });
- });
+ })
+ })
- if (!cssStr) return null;
- return cssStr;
+ if (!cssStr) return null
+ return cssStr
}
export function processNode(node: JsonTagElNode, usedClasses: Set): string | null {
- let cssStr = "";
+ let cssStr = ''
if (node.cr) {
- const classRecords = processClassRecords(node.cr, usedClasses);
- if (classRecords) cssStr += classRecords;
+ const classRecords = processClassRecords(node.cr, usedClasses)
+ if (classRecords) cssStr += classRecords
}
if (node.child) {
Object.values(node.child).forEach((childNode) => {
- const childNodeStr = processNode(childNode, usedClasses);
- if (childNodeStr) cssStr += childNodeStr;
- });
+ const childNodeStr = processNode(childNode, usedClasses)
+ if (childNodeStr) cssStr += childNodeStr
+ })
}
- return cssStr || null;
+ return cssStr || null
}
export function generateCSS<
@@ -132,28 +132,28 @@ export function generateCSS<
JsonTagElNode
>,
>(nodeMap: NodeMap): string | null {
- const usedClasses = new Set();
- let cssStr = "";
+ const usedClasses = new Set()
+ let cssStr = ''
Object.values(nodeMap).forEach((node) => {
- const nodeStr = processNode(node, usedClasses);
+ const nodeStr = processNode(node, usedClasses)
- if (nodeStr) cssStr += nodeStr;
- });
+ if (nodeStr) cssStr += nodeStr
+ })
- if (!cssStr) return null;
- return cssStr;
+ if (!cssStr) return null
+ return cssStr
}
export const createKeyVal = (key: Key, val: Val) => {
const obj = {
[key]: val,
} as {
- [K in Key]: Val;
- };
+ [K in Key]: Val
+ }
- return obj;
-};
+ return obj
+}
export const cssPropertyValueGen = <
ClassAbbrevKey extends string,
@@ -166,14 +166,14 @@ export const cssPropertyValueGen = <
value: Value,
unit: Unit,
) => {
- const cssGen = `${property}: ${value}${unit};` as const;
+ const cssGen = `${property}: ${value}${unit};` as const
return {
[classAbbrevKey]: cssGen,
} as {
- [K in ClassAbbrevKey]: typeof cssGen;
- };
-};
+ [K in ClassAbbrevKey]: typeof cssGen
+ }
+}
export const sizingHelper = (
classValKey: ClassValKey,
@@ -181,10 +181,10 @@ export const sizingHelper = {
return {
- ...cssPropertyValueGen(`w-${classValKey}`, "width", value, unit),
- ...cssPropertyValueGen(`h-${classValKey}`, "height", value, unit),
- } as const;
-};
+ ...cssPropertyValueGen(`w-${classValKey}`, 'width', value, unit),
+ ...cssPropertyValueGen(`h-${classValKey}`, 'height', value, unit),
+ } as const
+}
const fractionHelper = <
ClassAbbrevKey extends string,
@@ -195,17 +195,17 @@ const fractionHelper = <
fraction: Fraction,
property: Property,
) => {
- const percentageValue = fractionPercentMap[fraction];
+ const percentageValue = fractionPercentMap[fraction]
- return cssPropertyValueGen(`${classAbbrevKey}-${fraction}`, property, percentageValue, "%");
-};
+ return cssPropertyValueGen(`${classAbbrevKey}-${fraction}`, property, percentageValue, '%')
+}
export const sizeFractions = (fraction: Fraction) => {
return {
- ...fractionHelper("w", fraction, "width"),
- ...fractionHelper("h", fraction, "height"),
- };
-};
+ ...fractionHelper('w', fraction, 'width'),
+ ...fractionHelper('h', fraction, 'height'),
+ }
+}
export const spacingHelper = (
marginFactorKey: ClassAbbrevKey,
@@ -228,48 +228,48 @@ export const spacingHelper = {
- let adjustedColor;
+ let adjustedColor
if (index < 5) {
// Darken the color for shades 50-400
- adjustedColor = adjustBrightness(baseColor, factor);
+ adjustedColor = adjustBrightness(baseColor, factor)
} else {
// Lighten the color for shades 600-900
- adjustedColor = lightenColor(baseColor, factor);
+ adjustedColor = lightenColor(baseColor, factor)
}
- const shade = rgbToHex(adjustedColor.r, adjustedColor.g, adjustedColor.b);
- shades.push(shade);
- });
+ const shade = rgbToHex(adjustedColor.r, adjustedColor.g, adjustedColor.b)
+ shades.push(shade)
+ })
- return shades;
+ return shades
}
function lightenColor(color: { r: number; g: number; b: number }, factor: number): { r: number; g: number; b: number } {
@@ -317,93 +317,93 @@ function lightenColor(color: { r: number; g: number; b: number }, factor: number
r: Math.round(clamp(color.r + (255 - color.r) * (factor - 1), 0, 255)),
g: Math.round(clamp(color.g + (255 - color.g) * (factor - 1), 0, 255)),
b: Math.round(clamp(color.b + (255 - color.b) * (factor - 1), 0, 255)),
- };
+ }
}
export const generateVariablesForColor = (color: Color) => {
- const cssVariables: string[] = [];
+ const cssVariables: string[] = []
- const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900] as const;
+ const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900] as const
- const shadeColorArray = generateShades(color);
+ const shadeColorArray = generateShades(color)
for (let i = 0; i < shades.length; i++) {
- const shade = shades[i];
- const colorCode = shadeColorArray[i];
+ const shade = shades[i]
+ const colorCode = shadeColorArray[i]
- cssVariables.push(`--${color}-${shade}: ${colorCode};\n`);
+ cssVariables.push(`--${color}-${shade}: ${colorCode};\n`)
}
- return cssVariables.join("");
-};
+ return cssVariables.join('')
+}
export const generateColorVariables = () => {
- let cssVariables = ":root {\n";
+ let cssVariables = ':root {\n'
const colors = [
- "red",
- "orange",
- "yellow",
- "green",
- "blue",
- "indigo",
- "purple",
- "pink",
- "slate",
- "gray",
- "lightgray",
- "powderblue",
- "whitesmoke",
- ] as const;
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'indigo',
+ 'purple',
+ 'pink',
+ 'slate',
+ 'gray',
+ 'lightgray',
+ 'powderblue',
+ 'whitesmoke',
+ ] as const
for (let i = 0; i < colors.length; i++) {
- const color = colors[i];
- const typedColor = color;
- cssVariables += generateVariablesForColor(typedColor);
+ const color = colors[i]
+ const typedColor = color
+ cssVariables += generateVariablesForColor(typedColor)
}
- cssVariables += "}\n";
- return cssVariables;
-};
+ cssVariables += '}\n'
+ return cssVariables
+}
export const textColorGen = (color: Color, shade: Shade) => {
- return cssPropertyValueGen(`text-${color}-${shade}`, "color", `var(--${color}-${shade})`, "");
-};
+ return cssPropertyValueGen(`text-${color}-${shade}`, 'color', `var(--${color}-${shade})`, '')
+}
export const bgColorGen = (color: Color, shade: Shade) => {
- return cssPropertyValueGen(`bg-${color}-${shade}`, "background-color", `var(--${color}-${shade})`, "");
-};
+ return cssPropertyValueGen(`bg-${color}-${shade}`, 'background-color', `var(--${color}-${shade})`, '')
+}
export const borderColorGen = (color: Color, shade: Shade) => {
- return cssPropertyValueGen(`border-${color}-${shade}`, "border-color", `var(--${color}-${shade})`, "");
-};
+ return cssPropertyValueGen(`border-${color}-${shade}`, 'border-color', `var(--${color}-${shade})`, '')
+}
export const textColorStrokeGen = (color: Color, shade: Shade) => {
return cssPropertyValueGen(
`text-stroke-${color}-${shade}`,
- "-webkit-text-stroke-color",
+ '-webkit-text-stroke-color',
`var(--${color}-${shade})`,
- "",
- );
-};
+ '',
+ )
+}
const shadowSizes = {
- "shadow-sm": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
- shadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
- "shadow-md": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
- "shadow-lg": "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
- "shadow-xl": "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
- "shadow-2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
- "shadow-inner": "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
- "shadow-none": "0 0 #0000",
-} as const;
-
-type ShadowSize = keyof typeof shadowSizes;
+ 'shadow-sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ shadow: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ 'shadow-md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
+ 'shadow-lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
+ 'shadow-xl': '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
+ 'shadow-2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
+ 'shadow-inner': 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
+ 'shadow-none': '0 0 #0000',
+} as const
+
+type ShadowSize = keyof typeof shadowSizes
export const shadowGen = (size: Size) => {
- const shadowValue = shadowSizes[size];
- return cssPropertyValueGen(size, "box-shadow", shadowValue, "");
-};
+ const shadowValue = shadowSizes[size]
+ return cssPropertyValueGen(size, 'box-shadow', shadowValue, '')
+}
export const generatePropertiesForColor = (colorKey: Color) => {
return {
@@ -447,63 +447,63 @@ export const generatePropertiesForColor = (colorKey: Co
...textColorStrokeGen(colorKey, 700),
...textColorStrokeGen(colorKey, 800),
...textColorStrokeGen(colorKey, 900),
- };
-};
+ }
+}
// gaps are based on rem by default
const baseGapSizes = {
- "0": "0",
- "0.5": "0.125", // 2px
- "1": "0.25", // 4px
- "1.5": "0.375", // 6px
- "2": "0.5", // 8px
- "2.5": "0.625", // 10px
- "3": "0.75", // 12px
- "3.5": "0.875", // 14px
- "4": "1", // 16px
- "5": "1.25", // 20px
- "6": "1.5", // 24px
- "7": "1.75", // 28px
- "8": "2", // 32px
- "9": "2.25", // 36px
- "10": "2.5", // 40px
- "11": "2.75", // 44px
- "12": "3", // 48px
- "14": "3.5", // 56px
- "16": "4", // 64px
- "20": "5", // 80px
- "24": "6", // 96px
- "28": "7", // 112px
- "32": "8", // 128px
- "36": "9", // 144px
- "40": "10", // 160px
- "44": "11", // 176px
- "48": "12", // 192px
- "52": "13", // 208px
- "56": "14", // 224px
- "60": "15", // 240px
- "64": "16", // 256px
- "72": "18", // 288px
- "80": "20", // 320px
- "96": "24", // 384px
-} as const;
-
-type GapSize = keyof typeof baseGapSizes;
-
-const gapHelper = (key: Key) => cssPropertyValueGen(key, "gap", baseGapSizes[key], "rem");
-
-export const generateGapHelper = (gap: Gap, unit: Unit) => {
- const gapValue = baseGapSizes[gap];
-
- const baseGap = `gap-${gap}` as const;
- const xGap = `gap-x-${gap}` as const;
- const yGap = `gap-y-${gap}` as const;
+ '0': '0',
+ '0.5': '0.125', // 2px
+ '1': '0.25', // 4px
+ '1.5': '0.375', // 6px
+ '2': '0.5', // 8px
+ '2.5': '0.625', // 10px
+ '3': '0.75', // 12px
+ '3.5': '0.875', // 14px
+ '4': '1', // 16px
+ '5': '1.25', // 20px
+ '6': '1.5', // 24px
+ '7': '1.75', // 28px
+ '8': '2', // 32px
+ '9': '2.25', // 36px
+ '10': '2.5', // 40px
+ '11': '2.75', // 44px
+ '12': '3', // 48px
+ '14': '3.5', // 56px
+ '16': '4', // 64px
+ '20': '5', // 80px
+ '24': '6', // 96px
+ '28': '7', // 112px
+ '32': '8', // 128px
+ '36': '9', // 144px
+ '40': '10', // 160px
+ '44': '11', // 176px
+ '48': '12', // 192px
+ '52': '13', // 208px
+ '56': '14', // 224px
+ '60': '15', // 240px
+ '64': '16', // 256px
+ '72': '18', // 288px
+ '80': '20', // 320px
+ '96': '24', // 384px
+} as const
+
+type GapSize = keyof typeof baseGapSizes
+
+const gapHelper = (key: Key) => cssPropertyValueGen(key, 'gap', baseGapSizes[key], 'rem')
+
+export const generateGapHelper = (gap: Gap, unit: Unit) => {
+ const gapValue = baseGapSizes[gap]
+
+ const baseGap = `gap-${gap}` as const
+ const xGap = `gap-x-${gap}` as const
+ const yGap = `gap-y-${gap}` as const
return {
- ...cssPropertyValueGen(baseGap, "gap", gapValue, unit),
- ...cssPropertyValueGen(xGap, "column-gap", gapValue, unit),
- ...cssPropertyValueGen(yGap, "row-gap", gapValue, unit),
- };
+ ...cssPropertyValueGen(baseGap, 'gap', gapValue, unit),
+ ...cssPropertyValueGen(xGap, 'column-gap', gapValue, unit),
+ ...cssPropertyValueGen(yGap, 'row-gap', gapValue, unit),
+ }
// return {
// [baseGap]: `gap: ${gapValue}${unit};`,
@@ -513,383 +513,383 @@ export const generateGapHelper = {
- it("should add classes to attributes based on ClassRecord", () => {
+describe('classRecordPluginHandler', () => {
+ it('should add classes to attributes based on ClassRecord', () => {
const node: CRNode = {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
- "border-gray-50": true,
- "border-blue-100": false,
- "bg-blue-100": true,
+ '*': {
+ 'border-gray-50': true,
+ 'border-blue-100': false,
+ 'bg-blue-100': true,
},
},
attributes: {
- id: "sample-id",
+ id: 'sample-id',
},
- content: "Sample Content",
- };
- const processedNode = classRecordPluginHandler(node);
- expect(processedNode.attributes?.class).toBe("border-gray-50 bg-blue-100");
- });
+ content: 'Sample Content',
+ }
+ const processedNode = classRecordPluginHandler(node)
+ expect(processedNode.attributes?.class).toBe('border-gray-50 bg-blue-100')
+ })
- it("should not add classes to attributes if ClassRecord is not present", () => {
+ it('should not add classes to attributes if ClassRecord is not present', () => {
const node: CRNode = {
- tag: "div",
+ tag: 'div',
attributes: {
- id: "sample-id",
+ id: 'sample-id',
},
- content: "Sample Content",
- };
- const processedNode = classRecordPluginHandler(node);
- expect(processedNode.attributes?.class).toBe(undefined);
- });
-});
+ content: 'Sample Content',
+ }
+ const processedNode = classRecordPluginHandler(node)
+ expect(processedNode.attributes?.class).toBe(undefined)
+ })
+})
-describe("classRecordPlugin", () => {
+describe('classRecordPlugin', () => {
const sampleNodeMap: JsonHtmlNodeTree = {
div1: {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
- "bg-blue-100": true,
- "bg-gray-100": false,
- "bg-red-100": true,
+ '*': {
+ 'bg-blue-100': true,
+ 'bg-gray-100': false,
+ 'bg-red-100': true,
},
},
- content: "Hello World!",
+ content: 'Hello World!',
},
div2: {
- tag: "div",
+ tag: 'div',
cr: {
- "*": {
- "bg-blue-100": true,
+ '*': {
+ 'bg-blue-100': true,
},
},
- content: "Another div",
+ content: 'Another div',
},
- };
+ }
- it("should add classes based on the ClassRecord", () => {
- const renderedHtml = jsonToHtml(sampleNodeMap, [classRecordPlugin]);
- expect(renderedHtml).toContain('Hello World!
');
- expect(renderedHtml).toContain('Another div
');
- });
+ it('should add classes based on the ClassRecord', () => {
+ const renderedHtml = jsonToHtml(sampleNodeMap, [classRecordPlugin])
+ expect(renderedHtml).toContain('Hello World!
')
+ expect(renderedHtml).toContain('Another div
')
+ })
- it("should not add classes with a value of false", () => {
- const renderedHtml = jsonToHtml(sampleNodeMap, [classRecordPlugin]);
- expect(renderedHtml).not.toContain("class-two");
- });
-});
+ it('should not add classes with a value of false', () => {
+ const renderedHtml = jsonToHtml(sampleNodeMap, [classRecordPlugin])
+ expect(renderedHtml).not.toContain('class-two')
+ })
+})
-describe("Markdown Plugin with jsonToHtml", () => {
- it("should convert a simple markdown text to HTML", () => {
+describe('Markdown Plugin with jsonToHtml', () => {
+ it('should convert a simple markdown text to HTML', () => {
const input = {
sampleId: {
- tag: "div",
- markdown: "# Hello\nThis is **bold**.",
+ tag: 'div',
+ markdown: '# Hello\nThis is **bold**.',
},
- };
+ }
- const output = jsonToHtml(input, [markdownPlugin]);
+ const output = jsonToHtml(input, [markdownPlugin])
- const expectedOutput = ``;
- expect(output).toBe(expectedOutput);
- });
+ const expectedOutput = ``
+ expect(output).toBe(expectedOutput)
+ })
- it("should handle nodes without markdown", () => {
+ it('should handle nodes without markdown', () => {
const input = {
sampleId: {
- tag: "div",
- content: "Just a regular div.",
+ tag: 'div',
+ content: 'Just a regular div.',
},
- };
+ }
- const output = jsonToHtml(input, [markdownPlugin]);
+ const output = jsonToHtml(input, [markdownPlugin])
- const expectedOutput = `Just a regular div.
`;
- expect(output).toBe(expectedOutput);
- });
-});
+ const expectedOutput = `Just a regular div.
`
+ expect(output).toBe(expectedOutput)
+ })
+})
diff --git a/htmlody/htmlody-plugins.ts b/htmlody/htmlody-plugins.ts
index f980f31..62ee3c4 100644
--- a/htmlody/htmlody-plugins.ts
+++ b/htmlody/htmlody-plugins.ts
@@ -1,70 +1,70 @@
-import { convertMarkdownToHTML } from "../utils/text-utils";
-import { ExtensionRec, JsonTagElNode, ResponsiveClassRecord } from "./htmlody-types";
+import { convertMarkdownToHTML } from '../utils/text-utils'
+import { ExtensionRec, JsonTagElNode, ResponsiveClassRecord } from './htmlody-types'
// this will be the node that will be attached to our json node
export type ClassRecordAttributes = {
- cr?: ResponsiveClassRecord;
-};
+ cr?: ResponsiveClassRecord
+}
-export type CRNode = JsonTagElNode;
-export type MDNode = JsonTagElNode;
+export type CRNode = JsonTagElNode
+export type MDNode = JsonTagElNode
export interface HTMLodyPlugin {
// need to figure out if finall return type should be with or without the type
- processNode: (node: JsonTagElNode) => JsonTagElNode;
+ processNode: (node: JsonTagElNode) => JsonTagElNode
}
export const classRecordPluginHandler = (node: Node) => {
- let classes = "";
+ let classes = ''
if (node.cr) {
const responsiveClasses = Object.entries(node.cr)
.map(([breakpoint, classRecord]) => {
- const breakpointPrefix = breakpoint === "*" ? "" : `${breakpoint}_`;
+ const breakpointPrefix = breakpoint === '*' ? '' : `${breakpoint}_`
const classList = Object.entries(classRecord || {})
.filter(([, value]) => value)
.map(([key]) => `${breakpointPrefix}${key}`)
- .join(" ");
- return classList;
+ .join(' ')
+ return classList
})
- .join(" ");
+ .join(' ')
- classes = responsiveClasses;
+ classes = responsiveClasses
}
- if (classes === "") {
- return node;
+ if (classes === '') {
+ return node
}
return {
...node,
attributes: { ...node.attributes, class: classes },
- };
-};
+ }
+}
export const classRecordPlugin: HTMLodyPlugin = {
processNode: classRecordPluginHandler,
-};
+}
export type MarkdownAttributes = {
- markdown?: string;
-};
+ markdown?: string
+}
export const markdownPluginHandler = (node: Node): JsonTagElNode => {
if (node.markdown) {
- const htmlContent = convertMarkdownToHTML(node.markdown);
+ const htmlContent = convertMarkdownToHTML(node.markdown)
// remove the markdown attribute after processing
- const { markdown, ...remainingAttributes } = node.attributes || {};
+ const { markdown, ...remainingAttributes } = node.attributes || {}
return {
...node,
content: htmlContent,
attributes: remainingAttributes,
- };
+ }
}
- return node;
-};
+ return node
+}
export const markdownPlugin: HTMLodyPlugin = {
processNode: markdownPluginHandler,
-};
+}
diff --git a/htmlody/htmlody-types.ts b/htmlody/htmlody-types.ts
index 9e8db27..4b3b973 100644
--- a/htmlody/htmlody-types.ts
+++ b/htmlody/htmlody-types.ts
@@ -1,32 +1,32 @@
-import { HtmlTags } from "./constants";
-import type { CSSMapKeys } from "./css-engine";
+import { HtmlTags } from './constants'
+import type { CSSMapKeys } from './css-engine'
-export type Attributes = Record;
+export type Attributes = Record
export type ClassRecord = Partial<{
- [key in CSSMapKeys]: boolean;
-}>;
+ [key in CSSMapKeys]: boolean
+}>
-const breakpoints = ["sm", "md", "lg", "xl"] as const;
-export type Breakpoint = (typeof breakpoints)[number];
+const breakpoints = ['sm', 'md', 'lg', 'xl'] as const
+export type Breakpoint = (typeof breakpoints)[number]
export type ResponsiveClassRecord = {
- "*"?: ClassRecord;
- sm?: ClassRecord;
- md?: ClassRecord;
- lg?: ClassRecord;
- xl?: ClassRecord;
-};
+ '*'?: ClassRecord
+ sm?: ClassRecord
+ md?: ClassRecord
+ lg?: ClassRecord
+ xl?: ClassRecord
+}
-export type ExtensionRec = Record;
+export type ExtensionRec = Record
export type JsonTagElNode = {
- content?: string;
- child?: JsonHtmlNodeTree>;
- attributes?: Attributes;
- tag: HtmlTags;
-} & Omit;
+ content?: string
+ child?: JsonHtmlNodeTree>
+ attributes?: Attributes
+ tag: HtmlTags
+} & Omit
export type JsonHtmlNodeTree = {
- [id: string]: JsonTagElNode;
-};
+ [id: string]: JsonTagElNode
+}
diff --git a/htmlody/htmlody-utils.test.ts b/htmlody/htmlody-utils.test.ts
index b0bf9e7..b768a4e 100644
--- a/htmlody/htmlody-utils.test.ts
+++ b/htmlody/htmlody-utils.test.ts
@@ -1,125 +1,125 @@
-import { JsonTagElNode } from "bnkit/htmlody";
-import { describe, expect, it } from "bun:test";
-import { Attributes } from "./htmlody-types";
-import { collectClassNames, formatAttributes, nodeFactory, retrieveElement } from "./htmlody-utils";
+import { JsonTagElNode } from 'bnkit/htmlody'
+import { describe, expect, it } from 'bun:test'
+import { Attributes } from './htmlody-types'
+import { collectClassNames, formatAttributes, nodeFactory, retrieveElement } from './htmlody-utils'
-describe("formatAttributes", () => {
- it("should handle empty attributes", () => {
- const attributes: Attributes = {};
+describe('formatAttributes', () => {
+ it('should handle empty attributes', () => {
+ const attributes: Attributes = {}
// @ts-expect-error
- const formatted = formatAttributes(attributes, {});
- expect(formatted).toBe("");
- });
-});
+ const formatted = formatAttributes(attributes, {})
+ expect(formatted).toBe('')
+ })
+})
-describe("formatAttributes", () => {
- it("should format attributes into string", () => {
+describe('formatAttributes', () => {
+ it('should format attributes into string', () => {
const attributes: Attributes = {
id: `id-${Math.floor(Math.random() * 1000)}`,
- class: "sample-class",
- };
+ class: 'sample-class',
+ }
// @ts-expect-error
- const formatted = formatAttributes(attributes, {});
- expect(formatted).toContain(attributes.id);
- expect(formatted).toContain(attributes.class);
- });
-});
+ const formatted = formatAttributes(attributes, {})
+ expect(formatted).toContain(attributes.id)
+ expect(formatted).toContain(attributes.class)
+ })
+})
-describe("retrieveElement", () => {
- it("should retrieve an element from a JsonHtmlNodeMap", () => {
+describe('retrieveElement', () => {
+ it('should retrieve an element from a JsonHtmlNodeMap', () => {
const JsonHtmlNodeMap = {
div: {
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class",
+ class: 'sample-class',
},
- content: "Sample Content",
+ content: 'Sample Content',
},
- };
- const element = retrieveElement(JsonHtmlNodeMap, "div");
+ }
+ const element = retrieveElement(JsonHtmlNodeMap, 'div')
expect(element).toEqual({
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class",
+ class: 'sample-class',
},
- content: "Sample Content",
- });
- });
+ content: 'Sample Content',
+ })
+ })
- it("should return undefined if the element is not present in the JsonHtmlNodeMap", () => {
+ it('should return undefined if the element is not present in the JsonHtmlNodeMap', () => {
const JsonHtmlNodeMap = {
div: {
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class",
+ class: 'sample-class',
},
- content: "Sample Content",
+ content: 'Sample Content',
},
- };
+ }
// @ts-expect-error
- const element = retrieveElement(JsonHtmlNodeMap, "span");
- expect(element).toBeUndefined();
- });
-});
+ const element = retrieveElement(JsonHtmlNodeMap, 'span')
+ expect(element).toBeUndefined()
+ })
+})
-describe("nodeFactory", () => {
- it("should create a new node with the given configuration", () => {
+describe('nodeFactory', () => {
+ it('should create a new node with the given configuration', () => {
const nodeConfig = {
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class",
- id: "sample-id",
+ class: 'sample-class',
+ id: 'sample-id',
},
- content: "Sample Content",
- };
- const factory = nodeFactory(nodeConfig);
- const newNode = factory.create();
+ content: 'Sample Content',
+ }
+ const factory = nodeFactory(nodeConfig)
+ const newNode = factory.create()
expect(newNode).toEqual({
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class",
- id: "sample-id",
+ class: 'sample-class',
+ id: 'sample-id',
},
- content: "Sample Content",
- });
- });
-});
+ content: 'Sample Content',
+ })
+ })
+})
-describe("collectClassNames", () => {
- it("should add class names to uniqueClassNames set", () => {
+describe('collectClassNames', () => {
+ it('should add class names to uniqueClassNames set', () => {
const node = {
- tag: "div",
+ tag: 'div',
attributes: {
- class: "sample-class-1 sample-class-2",
+ class: 'sample-class-1 sample-class-2',
},
- content: "Sample Content",
- };
- const uniqueClassNames = new Set();
- collectClassNames(node, uniqueClassNames);
- expect(uniqueClassNames).toEqual(new Set(["sample-class-1", "sample-class-2"]));
- });
+ content: 'Sample Content',
+ }
+ const uniqueClassNames = new Set()
+ collectClassNames(node, uniqueClassNames)
+ expect(uniqueClassNames).toEqual(new Set(['sample-class-1', 'sample-class-2']))
+ })
- it("should not add class names to uniqueClassNames set if class attribute is not present", () => {
+ it('should not add class names to uniqueClassNames set if class attribute is not present', () => {
const node = {
- tag: "div",
- content: "Sample Content",
- };
- const uniqueClassNames = new Set();
- collectClassNames(node, uniqueClassNames);
- expect(uniqueClassNames).toEqual(new Set());
- });
+ tag: 'div',
+ content: 'Sample Content',
+ }
+ const uniqueClassNames = new Set()
+ collectClassNames(node, uniqueClassNames)
+ expect(uniqueClassNames).toEqual(new Set())
+ })
- it("should not add class names to uniqueClassNames set if class attribute is not a string", () => {
+ it('should not add class names to uniqueClassNames set if class attribute is not a string', () => {
const node: JsonTagElNode = {
- tag: "div",
+ tag: 'div',
attributes: {
// @ts-expect-error
class: 123,
},
- content: "Sample Content",
- };
- const uniqueClassNames = new Set();
- collectClassNames(node, uniqueClassNames);
- expect(uniqueClassNames).toEqual(new Set());
- });
-});
+ content: 'Sample Content',
+ }
+ const uniqueClassNames = new Set()
+ collectClassNames(node, uniqueClassNames)
+ expect(uniqueClassNames).toEqual(new Set())
+ })
+})
diff --git a/htmlody/htmlody-utils.ts b/htmlody/htmlody-utils.ts
index 5d1d247..033d143 100644
--- a/htmlody/htmlody-utils.ts
+++ b/htmlody/htmlody-utils.ts
@@ -1,13 +1,13 @@
-import { HtmlTags, htmlTagsSet } from "./constants";
-import { CRNode } from "./htmlody-plugins";
-import { Attributes, JsonHtmlNodeTree, JsonTagElNode } from "./htmlody-types";
+import { HtmlTags, htmlTagsSet } from './constants'
+import { CRNode } from './htmlody-plugins'
+import { Attributes, JsonHtmlNodeTree, JsonTagElNode } from './htmlody-types'
export const retrieveElement = (
JsonHtmlNodeMap: Structure,
element: keyof Structure,
) => {
- return JsonHtmlNodeMap[element];
-};
+ return JsonHtmlNodeMap[element]
+}
export const nodeFactory = <
Extension extends Record,
@@ -16,47 +16,47 @@ export const nodeFactory = <
>(
config: NodeConfigT,
) => {
- const createNode = (options?: Omit): NodeT => {
+ const createNode = (options?: Omit): NodeT => {
return {
...config,
...options,
- };
- };
+ }
+ }
return {
create: createNode,
- };
-};
+ }
+}
export function formatAttributes(attributes: Attributes): string {
return Object.entries(attributes)
.map(([key, value]) => `${key}="${value}"`)
- .join(" ");
+ .join(' ')
}
export function isValidHtmlTag(tagName: HtmlTags): boolean {
- return htmlTagsSet.has(tagName);
+ return htmlTagsSet.has(tagName)
}
export function isValidAttributesString(attributesStr: string): boolean {
// Regex pattern for valid attribute format: key="value"
- const attributePattern = /^(\s?[a-zA-Z-]+="[^"]*"\s?)+$/;
- return attributePattern.test(attributesStr);
+ const attributePattern = /^(\s?[a-zA-Z-]+="[^"]*"\s?)+$/
+ return attributePattern.test(attributesStr)
}
export function collectClassNames(node: JsonTagElNode, uniqueClassNames: Set) {
- if (node.attributes && typeof node.attributes.class === "string") {
- const classList = node.attributes.class.split(" ");
- classList.forEach((cls) => uniqueClassNames.add(cls));
+ if (node.attributes && typeof node.attributes.class === 'string') {
+ const classList = node.attributes.class.split(' ')
+ classList.forEach((cls) => uniqueClassNames.add(cls))
}
}
export const children = (children: JsonTagElNode[]) => {
- const returnChildren: JsonHtmlNodeTree = {};
+ const returnChildren: JsonHtmlNodeTree = {}
for (let i = 0; i < children.length; i++) {
- returnChildren[i] = children[i];
+ returnChildren[i] = children[i]
}
- return returnChildren;
-};
+ return returnChildren
+}
diff --git a/htmlody/index.ts b/htmlody/index.ts
index 41eabcb..f3c0d12 100644
--- a/htmlody/index.ts
+++ b/htmlody/index.ts
@@ -1,20 +1,9 @@
-export type {
- Attributes,
- ClassRecord,
- ExtensionRec,
- JsonHtmlNodeTree,
- JsonTagElNode,
-} from "./htmlody-types";
-export { children } from "./htmlody-utils";
+export type { Attributes, ClassRecord, ExtensionRec, JsonHtmlNodeTree, JsonTagElNode } from './htmlody-types'
+export { children } from './htmlody-utils'
-export { htmlodyBuilder, jsonToHtml } from "./json-to-html-engine";
+export { htmlodyBuilder, jsonToHtml } from './json-to-html-engine'
-export { classRecordPlugin, markdownPlugin } from "./htmlody-plugins";
-export type {
- CRNode,
- ClassRecordAttributes,
- HTMLodyPlugin,
- MDNode,
-} from "./htmlody-plugins";
+export { classRecordPlugin, markdownPlugin } from './htmlody-plugins'
+export type { CRNode, ClassRecordAttributes, HTMLodyPlugin, MDNode } from './htmlody-plugins'
-export { cc, uClass } from "./css-engine";
+export { cc, uClass } from './css-engine'
diff --git a/htmlody/json-to-html-engine.test.ts b/htmlody/json-to-html-engine.test.ts
index 8290518..e35309a 100644
--- a/htmlody/json-to-html-engine.test.ts
+++ b/htmlody/json-to-html-engine.test.ts
@@ -1,7 +1,7 @@
-import { describe, expect, it } from "bun:test";
-import { HTMLodyPlugin, classRecordPlugin, markdownPlugin } from "./htmlody-plugins";
-import { Attributes, JsonHtmlNodeTree } from "./htmlody-types";
-import { formatAttributes, isValidAttributesString } from "./htmlody-utils";
+import { describe, expect, it } from 'bun:test'
+import { HTMLodyPlugin, classRecordPlugin, markdownPlugin } from './htmlody-plugins'
+import { Attributes, JsonHtmlNodeTree } from './htmlody-types'
+import { formatAttributes, isValidAttributesString } from './htmlody-utils'
import {
getHtmlTags,
getValidatedAttributesStr,
@@ -12,74 +12,74 @@ import {
renderNodeToHtml,
renderNodeWithPlugins,
validateTagName,
-} from "./json-to-html-engine";
+} from './json-to-html-engine'
export function randomAttributes(): Attributes {
return {
id: `id-${Math.floor(Math.random() * 1000)}`,
class: `class-${Math.floor(Math.random() * 1000)}`,
- };
+ }
}
function expectHtmlToMatch(html: string, expected: string) {
- expect(html.replace(/\s+/g, "")).toBe(expected.replace(/\s+/g, ""));
+ expect(html.replace(/\s+/g, '')).toBe(expected.replace(/\s+/g, ''))
}
-describe("renderHtmlTag", () => {
- it("should render HTML tag with attributes and content", () => {
- const tagName = "div";
- const attributesStr = 'class="sample"';
- const content = "Hello World";
- const childrenHtml = "Child ";
+describe('renderHtmlTag', () => {
+ it('should render HTML tag with attributes and content', () => {
+ const tagName = 'div'
+ const attributesStr = 'class="sample"'
+ const content = 'Hello World'
+ const childrenHtml = 'Child '
const rendered = renderHtmlTag({
tagName,
attributesStr,
content,
childrenHtml,
- });
- expect(rendered).toContain(tagName);
- expect(rendered).toContain(attributesStr);
- expect(rendered).toContain(content);
- expect(rendered).toContain(childrenHtml);
- });
- describe("renderHtmlTag", () => {
- it("should throw error for invalid tags", () => {
- let errorOccurred = false;
+ })
+ expect(rendered).toContain(tagName)
+ expect(rendered).toContain(attributesStr)
+ expect(rendered).toContain(content)
+ expect(rendered).toContain(childrenHtml)
+ })
+ describe('renderHtmlTag', () => {
+ it('should throw error for invalid tags', () => {
+ let errorOccurred = false
try {
// @ts-expect-error
- renderHtmlTag({ validate: true, tagName: "invalidTag" });
+ renderHtmlTag({ validate: true, tagName: 'invalidTag' })
} catch (e) {
- errorOccurred = true;
+ errorOccurred = true
// @ts-expect-error
- expect(e.message).toBe("Invalid tag name provided: invalidTag"); // If you want to check the error message
+ expect(e.message).toBe('Invalid tag name provided: invalidTag') // If you want to check the error message
}
- expect(errorOccurred).toBe(true);
- });
- });
- it("should handle missing content and children", () => {
+ expect(errorOccurred).toBe(true)
+ })
+ })
+ it('should handle missing content and children', () => {
const nodeMap: JsonHtmlNodeTree = {
div_id1: {
- tag: "div",
+ tag: 'div',
attributes: randomAttributes(),
},
- };
- const rendered = jsonToHtml(nodeMap, []);
- expect(rendered).not.toContain("undefined");
- });
-});
-
-describe("jsonToHtml", () => {
- it("should apply plugins to nodes", () => {
+ }
+ const rendered = jsonToHtml(nodeMap, [])
+ expect(rendered).not.toContain('undefined')
+ })
+})
+
+describe('jsonToHtml', () => {
+ it('should apply plugins to nodes', () => {
const nodeMap = {
div: {
- tag: "div",
+ tag: 'div',
cr: {
- "sample-class": true,
+ 'sample-class': true,
},
- content: "Sample Content",
+ content: 'Sample Content',
},
- };
+ }
const plugins = [
{
// @ts-expect-error
@@ -89,27 +89,27 @@ describe("jsonToHtml", () => {
...node.attributes,
class: Object.keys(node.cr)
.filter((key) => node.cr[key])
- .join(" "),
- };
+ .join(' '),
+ }
}
- return node;
+ return node
},
},
- ];
- const html = jsonToHtml(nodeMap, plugins);
- expect(html).toBe('Sample Content
');
- });
+ ]
+ const html = jsonToHtml(nodeMap, plugins)
+ expect(html).toBe('Sample Content
')
+ })
- it("should throw an error if tag name is not provided", () => {
+ it('should throw an error if tag name is not provided', () => {
const nodeMap = {
div: {
attributes: {
- class: "sample-class",
+ class: 'sample-class',
},
- content: "Sample Content",
+ content: 'Sample Content',
},
- }; // @ts-expect-error
- const plugins = [];
+ } // @ts-expect-error
+ const plugins = []
expect(() =>
// @ts-expect-error
@@ -118,251 +118,248 @@ describe("jsonToHtml", () => {
}),
).toThrow(
'Tag name not provided for node. \n \n Content: Sample Content\n\n {\n "attributes": {\n "class": "sample-class"\n },\n "content": "Sample Content"\n}\n ',
- );
- });
+ )
+ })
- it("should render entire HTML structure from node map", () => {
+ it('should render entire HTML structure from node map', () => {
const nodeMap: JsonHtmlNodeTree = {
div_id1: {
- tag: "div",
- content: "Sample Content",
+ tag: 'div',
+ content: 'Sample Content',
attributes: {
- id: "sample-id",
- class: "sample-class",
+ id: 'sample-id',
+ class: 'sample-class',
},
child: {
span_id1: {
- tag: "span",
- content: "Child Content",
+ tag: 'span',
+ content: 'Child Content',
},
},
},
- };
-
- const rendered = jsonToHtml(nodeMap, []);
- expect(rendered).toContain(nodeMap.div_id1.content);
- expect(rendered).toContain(nodeMap.div_id1.attributes!.id);
- expect(rendered).toContain(nodeMap.div_id1.attributes!.class);
- expect(rendered).toContain(nodeMap.div_id1.child!.span_id1.content);
- });
-});
-
-describe("renderChildren", () => {
- it("should render children recursively", () => {
+ }
+
+ const rendered = jsonToHtml(nodeMap, [])
+ expect(rendered).toContain(nodeMap.div_id1.content)
+ expect(rendered).toContain(nodeMap.div_id1.attributes!.id)
+ expect(rendered).toContain(nodeMap.div_id1.attributes!.class)
+ expect(rendered).toContain(nodeMap.div_id1.child!.span_id1.content)
+ })
+})
+
+describe('renderChildren', () => {
+ it('should render children recursively', () => {
const children: JsonHtmlNodeTree = {
div_id: {
- tag: "div",
+ tag: 'div',
content: `content-${Math.floor(Math.random() * 1000)}`,
},
- };
- const rendered = renderChildrenNodes(children, []);
- expect(rendered).toContain(children.div_id.content);
- });
-});
+ }
+ const rendered = renderChildrenNodes(children, [])
+ expect(rendered).toContain(children.div_id.content)
+ })
+})
-describe("getValidatedTagName", () => {
+describe('getValidatedTagName', () => {
it("should return the tag name if it's valid", () => {
- const tagName = "div";
- const result = validateTagName(tagName);
- expect(result).toBe(tagName);
- });
+ const tagName = 'div'
+ const result = validateTagName(tagName)
+ expect(result).toBe(tagName)
+ })
- it("should throw an error for invalid tags", () => {
- expect(() => validateTagName("invalidTag")).toThrow("Invalid tag name provided: invalidTag");
- });
-});
+ it('should throw an error for invalid tags', () => {
+ expect(() => validateTagName('invalidTag')).toThrow('Invalid tag name provided: invalidTag')
+ })
+})
-describe("getValidatedAttributesStr", () => {
+describe('getValidatedAttributesStr', () => {
it("should return the attributes string if it's valid", () => {
- const attributesStr = 'id="test"';
- const result = getValidatedAttributesStr(attributesStr);
- expect(result).toBe(attributesStr);
- });
-
- it("should throw an error for invalid attributes string", () => {
- expect(() => getValidatedAttributesStr("invalid=attr")).toThrow("Invalid attributes string provided: invalid=attr");
- });
-});
-
-describe("getHtmlTags", () => {
- it("should return start and close tags for non-self closing tags", () => {
- const { startTag, closeTag } = getHtmlTags("div", 'class="test"');
- expect(startTag).toBe('');
- expect(closeTag).toBe("
");
- });
-
- it("should return only start tag for self-closing tags", () => {
- const { startTag, closeTag } = getHtmlTags("img", 'src="test.jpg"');
- expect(startTag).toBe(' ');
- expect(closeTag).toBe("");
- });
-});
-
-describe("formatAttributes", () => {
- it("should format an attributes object into a string", () => {
+ const attributesStr = 'id="test"'
+ const result = getValidatedAttributesStr(attributesStr)
+ expect(result).toBe(attributesStr)
+ })
+
+ it('should throw an error for invalid attributes string', () => {
+ expect(() => getValidatedAttributesStr('invalid=attr')).toThrow('Invalid attributes string provided: invalid=attr')
+ })
+})
+
+describe('getHtmlTags', () => {
+ it('should return start and close tags for non-self closing tags', () => {
+ const { startTag, closeTag } = getHtmlTags('div', 'class="test"')
+ expect(startTag).toBe('')
+ expect(closeTag).toBe('
')
+ })
+
+ it('should return only start tag for self-closing tags', () => {
+ const { startTag, closeTag } = getHtmlTags('img', 'src="test.jpg"')
+ expect(startTag).toBe(' ')
+ expect(closeTag).toBe('')
+ })
+})
+
+describe('formatAttributes', () => {
+ it('should format an attributes object into a string', () => {
const attributes = {
- id: "test",
- class: "sample",
- };
- const result = formatAttributes(attributes);
- expect(result).toBe('id="test" class="sample"');
- });
-});
-
-describe("isValidAttributesString", () => {
- it("should return true for valid attributes string", () => {
- const isValid = isValidAttributesString('id="test" class="sample"');
- expect(isValid).toBe(true);
- });
-
- it("should return false for invalid attributes string", () => {
- const isValid = isValidAttributesString("id=test class=sample");
- expect(isValid).toBe(false);
- });
-});
-
-describe("renderNodeToHtml", () => {
- it("should throw an error if tag name is not provided", () => {
+ id: 'test',
+ class: 'sample',
+ }
+ const result = formatAttributes(attributes)
+ expect(result).toBe('id="test" class="sample"')
+ })
+})
+
+describe('isValidAttributesString', () => {
+ it('should return true for valid attributes string', () => {
+ const isValid = isValidAttributesString('id="test" class="sample"')
+ expect(isValid).toBe(true)
+ })
+
+ it('should return false for invalid attributes string', () => {
+ const isValid = isValidAttributesString('id=test class=sample')
+ expect(isValid).toBe(false)
+ })
+})
+
+describe('renderNodeToHtml', () => {
+ it('should throw an error if tag name is not provided', () => {
const node = {
attributes: {
- class: "sample-class",
+ class: 'sample-class',
},
- content: "Sample Content",
- };
+ content: 'Sample Content',
+ }
// @ts-expect-error
- expect(() => renderNodeToHtml(node, [])).toThrow("Tag name not provided for node.");
- });
+ expect(() => renderNodeToHtml(node, [])).toThrow('Tag name not provided for node.')
+ })
- it("should handle nested children in the node", () => {
+ it('should handle nested children in the node', () => {
const node = {
- tag: "div",
+ tag: 'div',
attributes: {
- id: "sample-id",
- class: "sample-class",
+ id: 'sample-id',
+ class: 'sample-class',
},
children: {
span_id1: {
- tag: "span",
- content: "Child Content",
+ tag: 'span',
+ content: 'Child Content',
},
},
- };
+ }
- const rendered = renderNodeToHtml(node, []);
- expectHtmlToMatch(rendered, 'Child Content
');
- });
-});
+ const rendered = renderNodeToHtml(node, [])
+ expectHtmlToMatch(rendered, 'Child Content
')
+ })
+})
-describe("createNodeFactory", () => {
- const plugins = [classRecordPlugin, markdownPlugin] satisfies HTMLodyPlugin[];
+describe('createNodeFactory', () => {
+ const plugins = [classRecordPlugin, markdownPlugin] satisfies HTMLodyPlugin[]
- const { nodeFactory, renderNodeTreeToHtml, renderSingleNode, renderChildren } = htmlodyBuilder({ plugins });
+ const { nodeFactory, renderNodeTreeToHtml, renderSingleNode, renderChildren } = htmlodyBuilder({ plugins })
- describe("createNode", () => {
- it("should create a default node with a div tag", () => {
- const { div } = nodeFactory();
- const node = div();
+ describe('createNode', () => {
+ it('should create a default node with a div tag', () => {
+ const { div } = nodeFactory()
+ const node = div()
- node.content = "Sample Content";
+ node.content = 'Sample Content'
- expect(node.tag).toBe("div");
- });
- });
+ expect(node.tag).toBe('div')
+ })
+ })
//renderChildren
- describe("renderChildren", () => {
- it("should render children recursively", () => {
+ describe('renderChildren', () => {
+ it('should render children recursively', () => {
const children: JsonHtmlNodeTree = {
div_id: {
- tag: "div",
+ tag: 'div',
content: `content-${Math.floor(Math.random() * 1000)}`,
},
- };
- const rendered = renderChildren(children);
- expect(rendered).toContain(children.div_id.content);
- });
- });
-
- describe("renderHtml", () => {
- it("should render HTML from a node map", () => {
+ }
+ const rendered = renderChildren(children)
+ expect(rendered).toContain(children.div_id.content)
+ })
+ })
+
+ describe('renderHtml', () => {
+ it('should render HTML from a node map', () => {
const nodeMap = {
div_id1: {
- tag: "div",
- content: "Sample Content",
+ tag: 'div',
+ content: 'Sample Content',
attributes: {
- id: "sample-id",
- class: "sample-class",
+ id: 'sample-id',
+ class: 'sample-class',
},
children: {
span_id1: {
- tag: "span",
- content: "Child Content",
+ tag: 'span',
+ content: 'Child Content',
},
},
},
- };
+ }
- const html = renderNodeTreeToHtml(nodeMap);
- expectHtmlToMatch(
- html,
- 'Sample ContentChild Content
',
- );
- });
- });
+ const html = renderNodeTreeToHtml(nodeMap)
+ expectHtmlToMatch(html, 'Sample ContentChild Content
')
+ })
+ })
// check that markdown plugin works
- it("should convert markdown to HTML", () => {
- const { div } = nodeFactory();
+ it('should convert markdown to HTML', () => {
+ const { div } = nodeFactory()
const markdownDiv = div({
- markdown: "# Hello World!",
- });
- const html = renderSingleNode(markdownDiv);
- expectHtmlToMatch(html, "
Hello World! ");
- });
+ markdown: '# Hello World!',
+ })
+ const html = renderSingleNode(markdownDiv)
+ expectHtmlToMatch(html, '
Hello World! ')
+ })
- it("should apply plugins to the node", () => {
- const { div } = nodeFactory();
+ it('should apply plugins to the node', () => {
+ const { div } = nodeFactory()
const node = div({
cr: {
- "*": {
- "bg-blue-200": true,
+ '*': {
+ 'bg-blue-200': true,
},
},
- content: "Sample Content",
- });
+ content: 'Sample Content',
+ })
- const html = renderSingleNode(node);
- expectHtmlToMatch(html, 'Sample Content
');
- });
+ const html = renderSingleNode(node)
+ expectHtmlToMatch(html, 'Sample Content
')
+ })
- it("should render a single node to HTML", () => {
- const div = nodeFactory().div;
+ it('should render a single node to HTML', () => {
+ const div = nodeFactory().div
const node = div({
- content: "Sample Content",
+ content: 'Sample Content',
attributes: {
- id: "sample-id",
- class: "sample-class",
+ id: 'sample-id',
+ class: 'sample-class',
},
- });
- const html = renderNodeToHtml(node, []);
- expectHtmlToMatch(html, 'Sample Content
');
- });
-});
-
-describe("renderNodeWithPlugins", () => {
- it("should throw an error if tag name is not provided", () => {
+ })
+ const html = renderNodeToHtml(node, [])
+ expectHtmlToMatch(html, 'Sample Content
')
+ })
+})
+
+describe('renderNodeWithPlugins', () => {
+ it('should throw an error if tag name is not provided', () => {
const node = {
attributes: {
- id: "sample-id",
+ id: 'sample-id',
},
- content: "Sample Content",
- };
- const plugins = [];
+ content: 'Sample Content',
+ }
+ const plugins = []
expect(() => renderNodeWithPlugins(node, plugins)).toThrow(
'Tag name not provided for node. \n ID: id="sample-id"\n Content: Sample Content\n\n {\n "attributes": {\n "id": "sample-id"\n },\n "content": "Sample Content"\n}\n ',
- );
- });
-});
+ )
+ })
+})
diff --git a/htmlody/json-to-html-engine.ts b/htmlody/json-to-html-engine.ts
index 26b7464..0a497bf 100644
--- a/htmlody/json-to-html-engine.ts
+++ b/htmlody/json-to-html-engine.ts
@@ -1,39 +1,39 @@
-import { htmlRes, middlewareFactory } from "../server";
-import { HtmlTags, SELF_CLOSING_TAGS, htmlTags } from "./constants";
-import { generateCSS, generateColorVariables } from "./css-engine";
-import { HTMLodyPlugin } from "./htmlody-plugins";
-import { ExtensionRec, JsonHtmlNodeTree, JsonTagElNode } from "./htmlody-types";
-import { formatAttributes, isValidAttributesString, isValidHtmlTag } from "./htmlody-utils";
+import { htmlRes, middlewareFactory } from '../server'
+import { HtmlTags, SELF_CLOSING_TAGS, htmlTags } from './constants'
+import { generateCSS, generateColorVariables } from './css-engine'
+import { HTMLodyPlugin } from './htmlody-plugins'
+import { ExtensionRec, JsonHtmlNodeTree, JsonTagElNode } from './htmlody-types'
+import { formatAttributes, isValidAttributesString, isValidHtmlTag } from './htmlody-utils'
export function validateTagName(tagName: HtmlTags): string {
if (!isValidHtmlTag(tagName)) {
- throw new Error(`Invalid tag name provided: ${tagName}`);
+ throw new Error(`Invalid tag name provided: ${tagName}`)
}
- return tagName;
+ return tagName
}
export function getValidatedAttributesStr(attributesStr: string): string {
- if (attributesStr !== "" && !isValidAttributesString(attributesStr)) {
- throw new Error(`Invalid attributes string provided: ${attributesStr}`);
+ if (attributesStr !== '' && !isValidAttributesString(attributesStr)) {
+ throw new Error(`Invalid attributes string provided: ${attributesStr}`)
}
- return attributesStr;
+ return attributesStr
}
export function getHtmlTags(tagName: string, attributesStr: string): { startTag: string; closeTag: string } {
- const space = attributesStr ? " " : "";
+ const space = attributesStr ? ' ' : ''
// Check if the tag is a self-closing tag using Set lookup
if (SELF_CLOSING_TAGS.has(tagName)) {
return {
startTag: `<${tagName}${space}${attributesStr} />`,
- closeTag: "",
- };
+ closeTag: '',
+ }
}
return {
startTag: `<${tagName}${space}${attributesStr}>`,
closeTag: `${tagName}>`,
- };
+ }
}
export function renderHtmlTag({
@@ -43,24 +43,24 @@ export function renderHtmlTag({
tagName,
validate,
}: {
- tagName: HtmlTags;
- attributesStr: string;
- content: string;
- childrenHtml: string;
- validate?: boolean;
+ tagName: HtmlTags
+ attributesStr: string
+ content: string
+ childrenHtml: string
+ validate?: boolean
}): string {
if (validate) {
- validateTagName(tagName);
+ validateTagName(tagName)
}
- const validatedAttributesStr = getValidatedAttributesStr(attributesStr);
+ const validatedAttributesStr = getValidatedAttributesStr(attributesStr)
const { startTag, closeTag } = getHtmlTags(
// validatedTagName,
tagName,
validatedAttributesStr,
- );
+ )
- return `${startTag}${content}${childrenHtml}${closeTag}`;
+ return `${startTag}${content}${childrenHtml}${closeTag}`
}
export function renderChildrenNodes[]>(
@@ -69,7 +69,7 @@ export function renderChildrenNodes[]>(
): string {
return Object.entries(children)
.map(([childTagName, childNode]) => jsonToHtml({ [childTagName]: childNode }, plugins))
- .join("");
+ .join('')
}
function processNodeWithPlugins<
@@ -77,44 +77,44 @@ function processNodeWithPlugins<
PluginProps extends ExtensionRec,
Node extends JsonTagElNode = JsonTagElNode,
>(node: Node, plugins: Plugins): Node {
- let processedNode = { ...node };
+ let processedNode = { ...node }
for (const plugin of plugins) {
- processedNode = plugin.processNode(processedNode);
+ processedNode = plugin.processNode(processedNode)
}
- return processedNode;
+ return processedNode
}
export type JSONToHTMLOptions = {
- validateHtmlTags?: boolean;
-};
+ validateHtmlTags?: boolean
+}
export function renderNodeToHtml<
Plugins extends HTMLodyPlugin[],
PluginProps extends ExtensionRec,
Node extends JsonTagElNode = JsonTagElNode,
>(node: Node, plugins: Plugins, options?: JSONToHTMLOptions): string {
- node = processNodeWithPlugins(node, plugins);
+ node = processNodeWithPlugins(node, plugins)
- const idAttribute = node.attributes?.id ? `id="${node.attributes.id}"` : "";
+ const idAttribute = node.attributes?.id ? `id="${node.attributes.id}"` : ''
- const tagName = node.tag;
+ const tagName = node.tag
- const content = node?.content || "";
+ const content = node?.content || ''
if (!tagName) {
throw new Error(
`Tag name not provided for node.
- ${idAttribute ? `ID: ${idAttribute}` : ""}
- ${content ? `Content: ${content}` : ""}
+ ${idAttribute ? `ID: ${idAttribute}` : ''}
+ ${content ? `Content: ${content}` : ''}
${JSON.stringify(node, null, 2)}
`,
- );
+ )
}
- const attributesStr = formatAttributes(node.attributes || {});
- const childrenHtml = node.child ? renderChildrenNodes(node.child, plugins) : "";
+ const attributesStr = formatAttributes(node.attributes || {})
+ const childrenHtml = node.child ? renderChildrenNodes(node.child, plugins) : ''
- return renderHtmlTag({ tagName, attributesStr, content, childrenHtml });
+ return renderHtmlTag({ tagName, attributesStr, content, childrenHtml })
}
export function jsonToHtml<
@@ -125,7 +125,7 @@ export function jsonToHtml<
>(nodeMap: NodeMap, plugins: Plugins, options?: JSONToHTMLOptions): string {
return Object.keys(nodeMap)
.map((id) => renderNodeToHtml(nodeMap[id], plugins, options))
- .join("");
+ .join('')
}
export function renderNodeWithPlugins<
@@ -133,24 +133,24 @@ export function renderNodeWithPlugins<
PluginProps extends ExtensionRec,
Node extends JsonTagElNode = JsonTagElNode,
>(node: Node, plugins: Plugins, options?: JSONToHTMLOptions): string {
- node = processNodeWithPlugins(node, plugins);
+ node = processNodeWithPlugins(node, plugins)
- const tagName = node.tag;
- const content = node.content || "";
+ const tagName = node.tag
+ const content = node.content || ''
if (!tagName) {
- const idAttribute = node.attributes?.id ? `id="${node.attributes.id}"` : "";
+ const idAttribute = node.attributes?.id ? `id="${node.attributes.id}"` : ''
throw new Error(
`Tag name not provided for node.
- ${idAttribute ? `ID: ${idAttribute}` : ""}
- ${content ? `Content: ${content}` : ""}
+ ${idAttribute ? `ID: ${idAttribute}` : ''}
+ ${content ? `Content: ${content}` : ''}
${JSON.stringify(node, null, 2)}
`,
- );
+ )
}
- const attributesStr = formatAttributes(node.attributes || {});
- const childrenHtml = node.child ? renderChildrenNodes(node.child, plugins) : "";
+ const attributesStr = formatAttributes(node.attributes || {})
+ const childrenHtml = node.child ? renderChildrenNodes(node.child, plugins) : ''
return renderHtmlTag({
tagName,
@@ -158,104 +158,94 @@ export function renderNodeWithPlugins<
content,
childrenHtml,
validate: options?.validateHtmlTags,
- });
+ })
}
const generateTitleNode = (title: string): JsonTagElNode => {
return {
- tag: "title",
+ tag: 'title',
content: title,
- };
-};
+ }
+}
-const generateMetaTagNode = (meta: {
- name: string;
- content: string;
-}): JsonTagElNode => {
+const generateMetaTagNode = (meta: { name: string; content: string }): JsonTagElNode => {
return {
- tag: "meta",
+ tag: 'meta',
attributes: { name: meta.name, content: meta.content },
- };
-};
+ }
+}
-const generateLinkTagNode = (link: {
- rel: string;
- href: string;
-}): JsonTagElNode => {
+const generateLinkTagNode = (link: { rel: string; href: string }): JsonTagElNode => {
return {
- tag: "link",
+ tag: 'link',
attributes: { rel: link.rel, href: link.href },
- };
-};
+ }
+}
const generateStyleTagNode = (content: string): JsonTagElNode => {
return {
- tag: "style",
+ tag: 'style',
content,
- };
-};
-
-const generateScriptTagNode = (script: {
- src?: string;
- type?: string;
- content: string;
-}): JsonTagElNode => {
+ }
+}
+
+const generateScriptTagNode = (script: { src?: string; type?: string; content: string }): JsonTagElNode => {
const node: JsonTagElNode = {
- tag: "script",
+ tag: 'script',
attributes: {},
content: script.content,
- };
+ }
if (script.src && node.attributes) {
- node.attributes.src = script.src;
+ node.attributes.src = script.src
}
if (script.type && node.attributes) {
- node.attributes.type = script.type;
+ node.attributes.type = script.type
}
- return node;
-};
+ return node
+}
-export type NodePluginsMapper[]> = ReturnType;
+export type NodePluginsMapper[]> = ReturnType
type HeadConfig = {
- title: string;
- metaTags?: { name: string; content: string }[];
- linkTags?: { rel: string; href: string }[];
- styleTags?: { content: string }[];
- scriptTags?: { src?: string; type: string; content: string }[];
-};
+ title: string
+ metaTags?: { name: string; content: string }[]
+ linkTags?: { rel: string; href: string }[]
+ styleTags?: { content: string }[]
+ scriptTags?: { src?: string; type: string; content: string }[]
+}
type HTMLodyOptions = {
- middleware?: ReturnType;
-};
+ middleware?: ReturnType
+}
export const htmlodyNodeFactory = <
Plugins extends HTMLodyPlugin[],
NodeWithPlugins extends NodePluginsMapper,
- ReturnType extends Record) => JsonTagElNode> = Record<
+ ReturnType extends Record) => JsonTagElNode> = Record<
HtmlTags,
- (options?: Omit) => JsonTagElNode
+ (options?: Omit) => JsonTagElNode
>,
>(): ReturnType => {
- const create = (tag: HtmlTags, options?: Omit) => {
+ const create = (tag: HtmlTags, options?: Omit) => {
return {
tag,
- content: "",
+ content: '',
attributes: {},
...options,
- } as JsonTagElNode;
- };
+ } as JsonTagElNode
+ }
- const buildFns = {} as ReturnType;
+ const buildFns = {} as ReturnType
for (const tag of htmlTags) {
- buildFns[tag] = (options?: Omit) => create(tag, options);
+ buildFns[tag] = (options?: Omit) => create(tag, options)
}
- return buildFns;
-};
+ return buildFns
+}
export const htmlodyBuilder = <
Plugins extends HTMLodyPlugin[],
@@ -265,118 +255,118 @@ export const htmlodyBuilder = <
plugins,
options: builderOptions,
}: {
- plugins: Plugins;
+ plugins: Plugins
options?: {
allpages: {
- headConfig?: HeadConfig;
- };
- };
+ headConfig?: HeadConfig
+ }
+ }
}) => {
- const effectivePlugins = plugins;
+ const effectivePlugins = plugins
const nodeFactory = () => {
- return htmlodyNodeFactory();
- };
+ return htmlodyNodeFactory()
+ }
const inferTreeFn = <
Node extends JsonTagElNode = JsonTagElNode