diff --git a/.talismanrc b/.talismanrc index 9d5de6a..a07a811 100644 --- a/.talismanrc +++ b/.talismanrc @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..477c426 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. diff --git a/LICENSE b/LICENSE index 0c5a253..9535c61 100644 --- a/LICENSE +++ b/LICENSE @@ -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 @@ -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. \ No newline at end of file +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Models/metadata-model.ts b/src/Models/metadata-model.ts index e7210f9..31ec476 100644 --- a/src/Models/metadata-model.ts +++ b/src/Models/metadata-model.ts @@ -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) { diff --git a/src/endpoints.ts b/src/endpoints.ts index 224f90a..8fa0941 100644 --- a/src/endpoints.ts +++ b/src/endpoints.ts @@ -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 === '') { diff --git a/src/entry-editable.ts b/src/entry-editable.ts index 6492a08..ebb87bf 100644 --- a/src/entry-editable.ts +++ b/src/entry-editable.ts @@ -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 diff --git a/src/gql.ts b/src/gql.ts index f82f3b9..46a4f56 100644 --- a/src/gql.ts +++ b/src/gql.ts @@ -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 } \ No newline at end of file diff --git a/src/json-to-html.ts b/src/json-to-html.ts index 9bc46f3..1330c2d 100644 --- a/src/json-to-html.ts +++ b/src/json-to-html.ts @@ -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[], diff --git a/src/render-embedded-objects.ts b/src/render-embedded-objects.ts index 9aadfed..d091789 100644 --- a/src/render-embedded-objects.ts +++ b/src/render-embedded-objects.ts @@ -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[], @@ -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 diff --git a/src/updateAssetURLForGQL.ts b/src/updateAssetURLForGQL.ts index 29a3e4f..c72ec81 100644 --- a/src/updateAssetURLForGQL.ts +++ b/src/updateAssetURLForGQL.ts @@ -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;