diff --git a/js/README.md b/js/README.md index 8b4d1c38..09619d7e 100644 --- a/js/README.md +++ b/js/README.md @@ -75,6 +75,22 @@ bun test --watch ## Usage +### TypeScript Support + +This package includes TypeScript type definitions for improved developer experience with IntelliSense, autocomplete, and compile-time type checking. + +```typescript +import { Parser, Link, FormatOptions } from 'links-notation'; + +// TypeScript provides full type checking and autocomplete +const parser = new Parser({ + maxInputSize: 10 * 1024 * 1024, + maxDepth: 1000, +}); + +const links: Link[] = parser.parse('(source: type target)'); +``` + ### Basic Parsing ```javascript @@ -131,6 +147,45 @@ const group = new LinksGroup(parsed); console.log(group.format()); ``` +### TypeScript Usage Examples + +```typescript +import { + Parser, + Link, + FormatOptions, + FormatConfig, + formatLinks, +} from 'links-notation'; + +// Create parser with options +const parser = new Parser({ + maxInputSize: 5 * 1024 * 1024, + maxDepth: 500, +}); + +// Parse with full type safety +const links: Link[] = parser.parse('(id: value1 value2)'); + +// Create links programmatically +const link = new Link('parent', [new Link('child1'), new Link('child2')]); + +// Use formatting options +const formatOptions = new FormatOptions({ + lessParentheses: true, + maxLineLength: 80, + indentLongLines: true, + maxInlineRefs: 3, + groupConsecutive: false, +}); + +// Format with type-checked options +const formatted: string = link.format(formatOptions); + +// Format multiple links +const output: string = formatLinks(links, formatOptions); +``` + ## Syntax Examples ### Doublets (2-tuple) diff --git a/js/examples/typescript-example.ts b/js/examples/typescript-example.ts new file mode 100644 index 00000000..26ac8899 --- /dev/null +++ b/js/examples/typescript-example.ts @@ -0,0 +1,95 @@ +/** + * TypeScript example demonstrating usage of links-notation with type checking + */ + +import { + Parser, + Link, + FormatOptions, + FormatConfig, + formatLinks, +} from '../index.js'; + +// Example 1: Basic parsing +const parser = new Parser(); +const input = '(index: source target)'; +const links: Link[] = parser.parse(input); + +// Example 2: Creating links programmatically +const link1 = new Link('parent', [new Link('child1'), new Link('child2')]); + +console.log(link1.toString()); + +// Example 3: Working with link properties +const link2 = new Link('id', [new Link('value1'), new Link('value2')]); +console.log('ID:', link2.id); +console.log('Values:', link2.values); + +// Example 4: Link methods +const link3 = new Link('source'); +const link4 = new Link('target'); +const combined = link3.combine(link4); +console.log('Combined:', combined.toString()); + +// Example 5: FormatOptions +const formatOptions = new FormatOptions({ + lessParentheses: true, + maxLineLength: 80, + indentLongLines: true, + maxInlineRefs: 3, + groupConsecutive: false, + indentString: ' ', + preferInline: true, +}); + +// Example 6: Formatting with options +const formattedLink = link2.format(formatOptions); +console.log('Formatted:', formattedLink); + +// Example 7: FormatConfig (alias for FormatOptions) +const formatConfig = new FormatConfig({ + lessParentheses: false, + maxInlineRefs: 2, +}); + +// Example 8: Format multiple links +const multipleLinks = [ + new Link('link1', [new Link('a'), new Link('b')]), + new Link('link2', [new Link('c'), new Link('d')]), +]; + +const formatted = formatLinks(multipleLinks, formatConfig); +console.log('Formatted links:\n', formatted); + +// Example 9: Parser with options +const customParser = new Parser({ + maxInputSize: 5 * 1024 * 1024, // 5MB + maxDepth: 500, +}); + +// Example 10: Complex parsing +const complexInput = ` +parent + child1 + child2 + grandchild +`; + +const complexLinks = customParser.parse(complexInput); +complexLinks.forEach((link) => { + console.log(link.format(true)); +}); + +// Example 11: Link equality +const linkA = new Link('id', [new Link('value')]); +const linkB = new Link('id', [new Link('value')]); +console.log('Equal:', linkA.equals(linkB)); + +// Example 12: Link simplification +const complexLink = new Link(null, [new Link('single')]); +const simplified = complexLink.simplify(); +console.log('Simplified:', simplified.toString()); + +// Example 13: Working with quoted references +const quotedLink = new Link('quoted id', [new Link('value with spaces')]); +console.log('Escaped:', Link.escapeReference('needs:quoting')); diff --git a/js/index.d.ts b/js/index.d.ts new file mode 100644 index 00000000..8a6532fc --- /dev/null +++ b/js/index.d.ts @@ -0,0 +1,312 @@ +/** + * TypeScript type definitions for links-notation + * + * This module provides TypeScript type definitions for the Links Notation parser. + * Links Notation is a format for representing relationships between entities. + */ + +/** + * Represents a link with an optional identifier and values. + * + * A Link can represent: + * - A simple reference: `new Link('id')` + * - A link with values: `new Link('id', [new Link('child1'), new Link('child2')])` + * - A link without an id: `new Link(null, [...])` + */ +export class Link { + /** + * Optional identifier for the link + */ + id: string | null; + + /** + * Array of child links/values + */ + values: Link[]; + + /** + * Create a new Link + * @param id - Optional identifier for the link + * @param values - Optional array of nested links + * @throws {TypeError} If values is not an array or null + */ + constructor(id?: string | null, values?: Link[] | null); + + /** + * Convert link to string representation + * @returns String representation of the link + */ + toString(): string; + + /** + * Get formatted string of all values + * @returns Space-separated string of values + */ + getValuesString(): string; + + /** + * Simplify the link structure by unwrapping single-value containers + * @returns Simplified link + */ + simplify(): Link; + + /** + * Combine this link with another link + * @param other - The link to combine with + * @returns Combined link + */ + combine(other: Link): Link; + + /** + * Convert to string using either just ID or full format + * @returns String representation + */ + toLinkOrIdString(): string; + + /** + * Check equality with another Link + * @param other - Object to compare with + * @returns True if links are equal + */ + equals(other: any): boolean; + + /** + * Format the link as a string + * @param lessParentheses - If true, omit parentheses where safe; or a FormatOptions/FormatConfig object + * @param isCompoundValue - If true, this is a value in a compound link + * @returns Formatted string + */ + format( + lessParentheses?: boolean | FormatOptions | FormatConfig, + isCompoundValue?: boolean + ): string; + + /** + * Check if a string needs to be wrapped in parentheses + * @param str - The string to check + * @returns True if parentheses are needed + */ + needsParentheses(str: string): boolean; + + /** + * Get string representation of a value + * @param value - The value to stringify + * @returns String representation + */ + static getValueString(value: Link): string; + + /** + * Escape a reference string by adding quotes if necessary + * @param reference - The reference to escape + * @returns Escaped reference + */ + static escapeReference(reference: string): string; +} + +/** + * Parser options for configuring the parser behavior + */ +export interface ParserOptions { + /** + * Maximum input size in bytes (default: 10MB) + */ + maxInputSize?: number; + + /** + * Maximum nesting depth (default: 1000) + */ + maxDepth?: number; +} + +/** + * Parser for Links Notation format + * + * The Parser class converts Links Notation strings into Link objects. + */ +export class Parser { + /** + * Maximum input size in bytes + */ + maxInputSize: number; + + /** + * Maximum nesting depth + */ + maxDepth: number; + + /** + * Create a new Parser instance + * @param options - Parser options + */ + constructor(options?: ParserOptions); + + /** + * Parse Lino notation text into Link objects + * @param input - The Lino notation text to parse + * @returns Array of parsed Link objects + * @throws {Error} If parsing fails + */ + parse(input: string): Link[]; +} + +/** + * Options for formatting links + */ +export interface FormatOptionsConfig { + /** + * If true, omit parentheses where safe (default: false) + */ + lessParentheses?: boolean; + + /** + * Maximum line length before auto-indenting (default: 80) + */ + maxLineLength?: number; + + /** + * If true, indent lines exceeding maxLineLength (default: false) + */ + indentLongLines?: boolean; + + /** + * Maximum number of references before auto-indenting (default: null) + */ + maxInlineRefs?: number | null; + + /** + * If true, group consecutive links with same ID (default: false) + */ + groupConsecutive?: boolean; + + /** + * String to use for indentation (default: " ") + */ + indentString?: string; + + /** + * If true, prefer inline format when under thresholds (default: true) + */ + preferInline?: boolean; +} + +/** + * FormatOptions for Lino notation formatting. + * + * Provides configuration options for controlling how Link objects are formatted. + */ +export class FormatOptions { + /** + * If true, omit parentheses where safe + */ + lessParentheses: boolean; + + /** + * Maximum line length before auto-indenting + */ + maxLineLength: number; + + /** + * If true, indent lines exceeding maxLineLength + */ + indentLongLines: boolean; + + /** + * Maximum number of references before auto-indenting + */ + maxInlineRefs: number | null; + + /** + * If true, group consecutive links with same ID + */ + groupConsecutive: boolean; + + /** + * String to use for indentation + */ + indentString: string; + + /** + * If true, prefer inline format when under thresholds + */ + preferInline: boolean; + + /** + * Create a new FormatOptions instance + * @param options - Configuration options + */ + constructor(options?: FormatOptionsConfig); + + /** + * Check if line should be indented based on length + * @param line - The line to check + * @returns True if line should be indented based on length threshold + */ + shouldIndentByLength(line: string): boolean; + + /** + * Check if link should be indented based on reference count + * @param refCount - Number of references in the link + * @returns True if link should be indented based on reference count threshold + */ + shouldIndentByRefCount(refCount: number): boolean; +} + +/** + * FormatConfig is an alias for FormatOptions to maintain API consistency + * with the Python implementation. + * + * This is an alias for FormatOptions to maintain API consistency across languages. + * Python uses 'FormatConfig', so this export provides the same name for JavaScript. + */ +export class FormatConfig extends FormatOptions { + /** + * Create a new FormatConfig instance + * @param options - Configuration options + */ + constructor(options?: FormatOptionsConfig); +} + +/** + * Container for grouping related links + */ +export class LinksGroup { + /** + * The element associated with this group + */ + element: any; + + /** + * Child groups or links + */ + children: (LinksGroup | Link)[]; + + /** + * Create a new LinksGroup + * @param element - The element for this group + * @param children - Optional array of child groups or links + */ + constructor(element: any, children?: (LinksGroup | Link)[]); + + /** + * Convert the group to a flat list + * @returns Flattened list of elements + */ + toList(): any[]; + + /** + * Convert the group to a string representation + * @returns String representation of the group + */ + toString(): string; +} + +/** + * Format an array of links as a string + * @param links - Array of links to format + * @param lessParentheses - If true, omit parentheses where safe; or a FormatOptions/FormatConfig object + * @returns Formatted string with each link on a new line + */ +export function formatLinks( + links: Link[], + lessParentheses?: boolean | FormatOptions | FormatConfig +): string; diff --git a/js/package-lock.json b/js/package-lock.json index 4f86208e..b81f5489 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -11,7 +11,8 @@ "devDependencies": { "bun-types": "^1.2.19", "eslint": "^9.39.1", - "peggy": "^5.0.6" + "peggy": "^5.0.6", + "typescript": "^5.9.3" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1121,6 +1122,20 @@ "node": ">= 0.8.0" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", diff --git a/js/package.json b/js/package.json index 3d95b439..3841223e 100644 --- a/js/package.json +++ b/js/package.json @@ -3,6 +3,7 @@ "version": "0.13.0", "description": "Links Notation parser for JavaScript", "main": "dist/index.js", + "types": "index.d.ts", "type": "module", "scripts": { "build": "bun run build:grammar && bun build src/index.js --outdir=dist --target=node", @@ -23,7 +24,7 @@ "devDependencies": { "bun-types": "^1.2.19", "eslint": "^9.39.1", - "peggy": "^5.0.6" - }, - "dependencies": {} + "peggy": "^5.0.6", + "typescript": "^5.9.3" + } } diff --git a/js/tsconfig.json b/js/tsconfig.json new file mode 100644 index 00000000..7479d4bb --- /dev/null +++ b/js/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "checkJs": false, + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true + }, + "include": ["examples/**/*.ts"] +}