Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ fileignoreconfig:
checksum: d455330cc4f9306889fb299171364a37ad2c3bafe1fbd334033edc94f21694a6
- filename: package.json
checksum: 033eb21070795be5b426183f52d784347110fcb724bc9f8d63f94898ac5f0086
- filename: src/Models/metadata-model.ts
checksum: a101e109db1ec6ee0cb16a116f9b099e547c3104881e4fa1eaef2849e2d0aaf0
- filename: src/json-to-html.ts
checksum: a843710fc9f54bf4c7996f39561dc66491d62a9d9eeca50fa2c7c37bd6141f53
- filename: src/render-embedded-objects.ts
checksum: 35d56d4f7b625611fef18414fccdbff014c1d90d02e17eb0efa4d6166b73e23b
120 changes: 120 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Contributing to Contentstack Utils JavaScript

Thank you for your interest in contributing to Contentstack Utils JavaScript. This document provides guidelines and instructions for contributing.

## Pull Request Target Branch

**All pull requests must be raised against the `development` branch.**

Do not open PRs against `master` or `staging`. Create your feature or fix branch from `development`, and open your PR to merge into `development`. Maintainers will handle promotion to other branches after review.

## Getting Started

### Prerequisites

- **Node.js** 10 or later
- **npm** (comes with Node.js)
- **Git**

### Development Setup

1. **Fork the repository** on GitHub and clone your fork locally:

```bash
git clone https://github.com/YOUR_USERNAME/contentstack-utils-javascript.git
cd contentstack-utils-javascript
```

2. **Add the upstream remote** (optional, for syncing with the main repo):

```bash
git remote add upstream https://github.com/contentstack/contentstack-utils-javascript.git
```

3. **Create a branch from `development`** for your work:

```bash
git fetch upstream
git checkout development
git pull upstream development
git checkout -b your-feature-or-fix-name
```

4. **Install dependencies:**

```bash
npm install
```

5. **Build the project:**

```bash
npm run build
```

## Development Workflow

### Running Tests

- Run the full test suite: `npm test`
- Run tests in watch mode (for development): `npm run test:debug`

All tests must pass before submitting a PR. New features and bug fixes should include or update tests as appropriate.

### Code Style

- **ESLint:** The project uses ESLint. Fix auto-fixable issues with your editor or by running the linter.
- **Prettier:** Code is formatted with Prettier. Use `npm run format` to format `src/**/*.ts`.

Ensure your code adheres to the existing style so that CI and pre-commit checks pass.

### Commit Messages

This project uses [Conventional Commits](https://www.conventionalcommits.org/) enforced by Commitlint.

- Use a **type** and a **short subject** (e.g. `feat: add jsonToHTML option`, `fix: handle empty nodes`).
- Allowed types include: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `sample`.
- Subject should be lowercase, imperative, and not end with a period.
- Add a blank line and a longer body when the change needs more explanation.

Husky runs a commit-msg hook to validate commit messages. Invalid messages will be rejected.

### Pre-commit Hooks

Husky is used for Git hooks. Before each commit, the pre-commit hook runs. Ensure your working tree is clean and that tests and lint pass locally to avoid failed commits.

## Submitting Changes

1. **Keep your branch up to date** with `development`:

```bash
git fetch upstream
git rebase upstream/development
```

2. **Open a Pull Request** against the **`development`** branch (not `master` or `staging`).

3. **Fill out the PR template** (if one exists) and provide:
- A clear title and description of the change
- Link to any related issue
- Summary of testing done

4. **Address review feedback** promptly. Maintainers may request changes before merging.

5. **Do not force-push** after review has started unless the maintainer asks you to; use new commits for updates when possible so review history is preserved.

## Reporting Issues

- Use the GitHub issue tracker for bugs and feature requests.
- Search existing issues first to avoid duplicates.
- Include steps to reproduce for bugs, and your environment (Node version, OS).
- For security issues, see [SECURITY.md](SECURITY.md).

## Additional Resources

- [README](README.md) – Project overview and usage
- [CHANGELOG](CHANGELOG.md) – Version history and changes
- [SECURITY](SECURITY.md) – Security and vulnerability reporting
- [CODEOWNERS](CODEOWNERS) – Code ownership and review expectations

Thank you for contributing.
13 changes: 6 additions & 7 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
The MIT License (MIT)
MIT License


Copyright (c) 2016-2025 Contentstack
Copyright (c) 2016-2026 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -10,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
9 changes: 9 additions & 0 deletions src/Models/metadata-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ export function nodeToMetadata(attribute: Attributes, textNode: TextNode): Metad
};
}

/**
* Serializes an Attributes object to a string of HTML attribute key-value pairs
* (e.g. ` key1="value1" key2="value2"`). Keys containing forbidden characters
* are skipped. Values are HTML-entity-encoded. Arrays are joined with `, `;
* nested objects are serialized as `key:value;` pairs.
*
* @param attributes - The attributes object to serialize (e.g. from a node or metadata).
* @returns A string starting with a space, followed by `key="value"` pairs suitable for inclusion in an HTML tag.
*/
export function attributeToString(attributes: Attributes): string {
let result = '';
for (const key in attributes) {
Expand Down
11 changes: 11 additions & 0 deletions src/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ export interface RegionsResponse {
regions: RegionData[];
}

/**
* Returns the Contentstack API endpoint(s) for a given region and optional service.
* Region can be an ID (e.g. `'us'`, `'eu'`) or an alias. Throws if the region is
* invalid or empty.
*
* @param region - Region ID or alias (e.g. `'us'`, `'eu'`). Default: `'us'`.
* @param service - Optional service name to return a single endpoint (e.g. `'delivery'`). If empty, returns all endpoints for the region.
* @param omitHttps - If true, strips the `https://` prefix from the returned URL(s). Default: false.
* @returns A single endpoint URL string if `service` is provided, otherwise the full endpoints object for the region.
* @throws Error if region is empty, invalid, or if the requested service is not found.
*/
export function getContentstackEndpoint(region: string = 'us', service: string = '', omitHttps: boolean = false): string | ContentstackEndpoints {
// Validate empty region before any processing
if (region === '') {
Expand Down
12 changes: 12 additions & 0 deletions src/entry-editable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ interface AppliedVariants {
metaKey: string
}

/**
* Adds Contentstack Live Preview (CSLP) data tags to an entry for editable UIs.
* Mutates the entry by attaching a `$` property with tag strings or objects
* (e.g. `data-cslp` / `data-cslp-parent-field`) for each field, including nested
* objects and references. Supports variant-aware tagging when the entry has
* applied variants.
*
* @param entry - The entry (EmbeddedItem) to tag. Must have uid and optional system/applied variants.
* @param contentTypeUid - Content type UID (e.g. `blog_post`). Used as part of the tag path.
* @param tagsAsObject - If true, tags are stored as objects (e.g. `{ "data-cslp": "..." }`); if false, as strings (e.g. `data-cslp=...`).
* @param locale - Locale code for the tag path (default: `'en-us'`).
*/
export function addTags(entry: EntryModel, contentTypeUid: string, tagsAsObject: boolean, locale: string = 'en-us'): void {
if (entry) {
// handle case senstivity for contentTypeUid and locale
Expand Down
16 changes: 16 additions & 0 deletions src/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ function enumerateKeys(option: {
}))
}
}

/**
* GraphQL API utilities for Contentstack. Provides methods to work with
* content fetched via the GraphQL API, including rendering Supercharged RTE
* (JSON) with embedded items from the GQL response.
*/
export const GQL = {
/**
* Converts Supercharged RTE (JSON) content to HTML for entries from a GraphQL response.
* Uses `embedded_itemsConnection.edges` to resolve embedded items. Mutates the entry
* JSON in-place by replacing JSON RTE content with the generated HTML.
*
* @param option - Configuration for conversion.
* @param option.entry - Entry or array of entries (EmbeddedItem) from a GQL response with JSON RTE and embedded_itemsConnection.
* @param option.paths - Key paths to the JSON RTE fields on the entry.
* @param option.renderOption - Optional render options to customize how nodes and embedded items are rendered to HTML.
*/
jsonToHTML
}
11 changes: 11 additions & 0 deletions src/json-to-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ import { enumerate, enumerateContents } from './helper/enumerate-entries';

export type AnyNode = TextNode | Node;

/**
* Converts Supercharged RTE (JSON) content to HTML for one or more entries.
* Walks the given paths on each entry, finds JSON RTE content, resolves embedded
* items from the entry, and renders nodes using the optional renderOption. Mutates
* the entry JSON in-place by replacing content with the generated HTML.
*
* @param option - Configuration for conversion.
* @param option.entry - Entry or array of entries that contain Supercharged RTE (JSON) fields.
* @param option.paths - Key paths to the JSON RTE fields (e.g. `['rte_field_uid', 'group.rte_uid']`).
* @param option.renderOption - Optional render options to customize how nodes and embedded items are rendered to HTML.
*/
export function jsonToHTML(option: {
entry: EntryEmbedable| EntryEmbedable[],
paths: string[],
Expand Down
23 changes: 15 additions & 8 deletions src/render-embedded-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { findEmbeddedItems, findRenderString } from './helper/find-embeded-objec
import { EntryEmbedable } from './Models/embedded-object';
import { findRenderContent } from './helper/find-render-content';
/**
*
* @param {EntryEmbedable| EntryEmbedable[]} entry - Objects that contains RTE with embedded objects
* @param {string[]} paths - Key paths for RTE contents in Entry object
* @param {RenderOption?} renderOption - Optional render options to render content
* Renders RTE (Rich Text Editor) content with embedded objects in-place.
* Mutates the entry/entries by replacing embedded item tags with HTML produced
* by the provided render options. Works with a single entry or an array of entries.
*
* @param option - Configuration for rendering.
* @param option.entry - Entry or array of entries containing RTE fields with embedded objects.
* @param option.renderOption - Optional render options (node/item handlers) to produce HTML for embedded content.
* @param option.paths - Optional key paths to specific RTE fields. If omitted, all RTE paths on the entry are rendered.
*/
export function render(option: {
entry: EntryEmbedable| EntryEmbedable[],
Expand Down Expand Up @@ -46,10 +50,13 @@ export function render(option: {
}

/**
*
* @param {string | string[]} content - RTE content to render
* @param {EntryEmbedable} options.entry - Entry object containing embedded objects
* @param {RenderOption?} options.renderOption - Optional render options to render content
* Renders a single RTE content string or array of strings by replacing embedded
* item tags with HTML. Uses the entry and renderOption from the given option to
* resolve embedded references and produce output.
*
* @param content - RTE content string or array of strings containing embedded item tags.
* @param option - Must include the entry (for resolving embedded items) and optionally renderOption.
* @returns The same shape as content: a string or array of strings with embedded tags replaced by rendered HTML.
*/
export function renderContent(content: (string | string[]), option: Option): (string| string[]) {
// return blank if content not present
Expand Down
9 changes: 9 additions & 0 deletions src/updateAssetURLForGQL.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* Updates asset URLs in a GraphQL response in-place. Walks the response data,
* finds RTE fields that have `embedded_itemsConnection`, and sets each
* embedded asset's `asset-link` attribute in the JSON to the asset's `url`
* from the response. Use after fetching content via GraphQL so RTE JSON
* contains correct asset URLs for rendering.
*
* @param gqlResponse - The raw GraphQL response object (e.g. `{ data: { ... } }`). Modified in place.
*/
export function updateAssetURLForGQL(gqlResponse:any) {
try {
const response = gqlResponse?.data;
Expand Down
Loading