UNPKG

@terrazzo/parser

Version:

Parser/validator for the Design Tokens Community Group (DTCG) standard.

689 lines (688 loc) 24.7 kB
import { AliasToken, AliasValue, BooleanToken, BooleanTokenNormalized, BooleanValue, BorderToken, BorderTokenNormalized, BorderValue, BorderValueNormalized, ColorSpace, ColorToken, ColorTokenNormalized, ColorValue, ColorValueNormalized, CubicBezierToken, CubicBezierTokenNormalized, CubicBezierValue, CustomTransformOptions, DimensionToken, DimensionTokenNormalized, DimensionValue, DurationToken, DurationTokenNormalized, DurationValue, FontFamilyToken, FontFamilyTokenNormalized, FontFamilyValue, FontFamilyValueNormalized, FontWeightToken, FontWeightTokenNormalized, FontWeightValue, FontWeightValueNormalized, GradientStop, GradientStopNormalized, GradientToken, GradientTokenNormalized, GradientValue, GradientValueNormalized, Group, GroupCore, GroupOrToken, LinkToken, LinkTokenNormalized, LinkValue, ModeMap, NumberToken, NumberTokenNormalized, NumberValue, ShadowToken, ShadowTokenNormalized, ShadowValue, ShadowValueNormalized, StringToken, StringTokenNormalized, StringValue, StrokeStyleToken, StrokeStyleTokenNormalized, StrokeStyleValue, StrokeStyleValueExpanded, Token, Token as Token$1, TokenCore, TokenMode, TokenNormalized, TokenNormalized as TokenNormalized$1, TokenNormalizedCore, TokenNormalizedSet, TokenNormalizedSet as TokenNormalizedSet$1, TokensSet, TransitionToken, TransitionTokenNormalized, TransitionValue, TransitionValueNormalized, TypographyToken, TypographyTokenNormalized, TypographyValue, TypographyValueNormalized } from "@terrazzo/token-tools"; import { AnyNode, DocumentNode, MemberNode, ObjectNode, ParseOptions as ParseOptions$1, ValueNode } from "@humanwhocodes/momoa"; import ytm from "yaml-to-momoa"; //#region src/logger.d.ts declare const LOG_ORDER: readonly ["error", "warn", "info", "debug"]; type LogSeverity = 'error' | 'warn' | 'info' | 'debug'; type LogLevel = LogSeverity | 'silent'; type LogGroup = 'config' | 'parser' | 'lint' | 'plugin' | 'server'; interface LogEntry { /** Originator of log message */ group: LogGroup; /** Error message to be logged */ message: string; /** Prefix message with label */ label?: string; /** File in disk */ filename?: URL; /** * Continue on error? * @default false */ continueOnError?: boolean; /** Show a code frame for the erring node */ node?: AnyNode; /** To show a code frame, provide the original source code */ src?: string; } interface DebugEntry { group: LogGroup; /** Error message to be logged */ message: string; /** Current subtask or submodule */ label?: string; /** Show code below message */ codeFrame?: { src: string; line: number; column: number; }; /** Display performance timing */ timing?: number; } /** * @param {Entry} entry * @param {Severity} severity * @return {string} */ declare function formatMessage(entry: LogEntry, severity: LogSeverity): string; declare class Logger { level: LogLevel; debugScope: string; errorCount: number; warnCount: number; infoCount: number; debugCount: number; constructor(options?: { level?: LogLevel; debugScope?: string; }); setLevel(level: LogLevel): void; /** Log an error message (always; can’t be silenced) */ error(entry: LogEntry): void; /** Log an info message (if logging level permits) */ info(entry: LogEntry): void; /** Log a warning message (if logging level permits) */ warn(entry: LogEntry): void; /** Log a diagnostics message (if logging level permits) */ debug(entry: DebugEntry): void; /** Get stats for current logger instance */ stats(): { errorCount: number; warnCount: number; infoCount: number; debugCount: number; }; } declare class TokensJSONError extends Error { constructor(message: string); } //#endregion //#region src/types.d.ts interface BuildHookOptions { /** Map of tokens */ tokens: Record<string, TokenNormalized$1>; /** Query transformed values */ getTransforms(params: TransformParams): TokenTransformed[]; /** Momoa documents */ sources: InputSource[]; outputFile: (/** Filename to output (relative to outDir) */ filename: string, /** Contents to write to file */ contents: string | Buffer) => void; } interface BuildRunnerResult { outputFiles: OutputFile[]; } interface BuildEndHookOptions { /** Map of tokens */ tokens: Record<string, TokenNormalized$1>; /** Query transformed values */ getTransforms(params: TransformParams): TokenTransformed[]; /** Momoa documents */ sources: InputSource[]; /** Final files to be written */ outputFiles: OutputFileExpanded[]; } interface Config { /** * Path to tokens.json * @default "./tokens.json" */ tokens?: string | string[]; /** * Output directory * @default "./tokens/" */ outDir?: string; /** Specify plugins */ plugins?: Plugin[]; /** Specify linting settings */ lint?: { /** Configure build behavior */ build?: { /** * Should linters run with `tz build`? * @default true */ enabled?: boolean; }; /** Configure lint rules */ rules?: Record<string, LintRuleShorthand | LintRuleLonghand>; }; /** Ignore token groups */ ignore?: { /** Token patterns to ignore. Accepts globs. */ tokens?: string[]; /** Ignore deprecated tokens */ deprecated?: boolean; }; } interface ConfigInit { tokens: URL[]; outDir: URL; plugins: Plugin[]; lint: { build: NonNullable<NonNullable<Config['lint']>['build']>; rules: Record<string, LintRuleLonghand>; }; ignore: { tokens: NonNullable<NonNullable<Config['ignore']>['tokens']>; deprecated: NonNullable<NonNullable<Config['ignore']>['deprecated']>; }; } interface ConfigOptions { logger?: Logger; /** @terrazzo/parser needs cwd so this can be run without Node.js. Importing defineConfig from @terrazzo/cli doesn’t need this. */ cwd: URL; } interface InputSource { filename?: URL; src: any; document: DocumentNode; } interface LintNotice { /** Lint message shown to the user */ message: string; /** Erring node (used to point to a specific line) */ node?: AnyNode; } type LintRuleSeverity = 'error' | 'warn' | 'off'; type LintRuleShorthand = LintRuleSeverity | 0 | 1 | 2; type LintRuleLonghand = [LintRuleSeverity | 0 | 1 | 2, any]; interface LintRuleNormalized<O = any> { id: string; severity: LintRuleSeverity; options?: O; } type LintReportDescriptor<MessageIds extends string> = { /** To error on a specific token source file, provide an erring node */ node?: AnyNode; /** To error on a specific token source file, also provide the source */ source?: InputSource; /** Provide data for messages */ data?: Record<string, unknown>; } & ({ /** Provide the error message to display */ message: string; messageId?: never; } | { message?: never; /** Provide the error message ID */ messageId: MessageIds; }); interface LintRule<MessageIds extends string, LintRuleOptions extends object | undefined = undefined, LintRuleDocs = unknown> { meta?: LintRuleMetaData<MessageIds, LintRuleOptions, LintRuleDocs>; /** * Function which returns an object with methods that ESLint calls to “visit” * nodes while traversing the abstract syntax tree. */ create(context: Readonly<LintRuleContext<MessageIds, LintRuleOptions>>): void | Promise<void>; /** * Default options the rule will be run with */ defaultOptions: LintRuleOptions; } interface LintRuleContext<MessageIds extends string, LintRuleOptions extends object | undefined = undefined> { /** The rule ID. */ id: string; /** * An array of the configured options for this rule. This array does not * include the rule severity. */ options: LintRuleOptions; /** The current working directory. */ cwd?: URL; /** Source file the token came from. */ src: string; /** Source file location. */ filename?: URL; /** ID:Token map of all tokens. */ tokens: Record<string, TokenNormalized$1>; /** Reports a problem in the code. */ report(descriptor: LintReportDescriptor<MessageIds>): void; } interface LintRuleMetaData<MessageIds extends string, LintRuleOptions extends object | undefined = undefined, LintRuleDocs = unknown> { /** * Documentation for the rule */ docs?: LintRuleDocs & LintRuleMetaDataDocs; /** * A map of messages which the rule can report. The key is the messageId, and * the string is the parameterised error string. */ messages?: Record<MessageIds, string>; /** * Specifies default options for the rule. If present, any user-provided * options in their config will be merged on top of them recursively. This * merging will be applied directly to `context.options`. */ defaultOptions?: LintRuleOptions; } interface LintRuleMetaDataDocs { /** Concise description of the rule. */ description: string; /** The URL of the rule's docs. */ url?: string; } interface OutputFile { /** Filename, relative to outDir */ filename: string; /** File contents */ contents: string | Buffer; /** Plugin name that generated the file */ plugin?: string; /** Time taken to generate file */ time?: number; } interface OutputFileExpanded extends OutputFile { /** The `name` of the plugin that produced this file. */ plugin: string; /** How long this output took to make. */ time: number; } interface Plugin { name: string; /** Read config, and optionally modify */ config?(config: ConfigInit): void | ConfigInit | undefined; /** * Declare: * - `"pre"`: run this plugin BEFORE all others * - `"post"`: run this plugin AFTER all others * - (default) run this plugin in default order (array order) */ enforce?: 'pre' | 'post'; /** Throw lint errors/warnings */ lint?(): Record<string, LintRule<any, any, any>>; transform?(options: TransformHookOptions): Promise<void>; build?(options: BuildHookOptions): Promise<void>; buildEnd?(result: BuildRunnerResult): Promise<void>; } /** Transformed token with a single value. Note that this may be any type! */ interface TokenTransformedSingleValue { /** Original Token ID */ id: string; /** ID unique to this format. */ localID?: string; type: 'SINGLE_VALUE'; value: string; /** * The mode of this value * @default "." */ mode: string; /** The original token. */ token: TokenNormalized$1; } /** Transformed token with multiple values. Note that this may be any type! */ interface TokenTransformedMultiValue { /** Original Token ID */ id: string; /** ID unique to this format.*/ localID?: string; type: 'MULTI_VALUE'; value: Record<string, string>; /** * The mode of this value * @default "." */ mode: string; /** The original token */ token: TokenNormalized$1; } type TokenTransformed = TokenTransformedSingleValue | TokenTransformedMultiValue; interface TransformParams { /** ID of an existing format */ format: string; /** Glob of tokens to select (e.g. `"color.*"` to select all tokens starting with `"color."`) */ id?: string | string[]; /** $type(s) to filter for */ $type?: string | string[]; /** * Mode name, if selecting a mode * @default "." */ mode?: string | string[]; } interface TransformHookOptions { /** Map of tokens */ tokens: Record<string, TokenNormalized$1>; /** Query transformed values */ getTransforms(params: TransformParams): TokenTransformed[]; /** Update transformed values */ setTransform(id: string, params: { format: string; localID?: string; value: string | Record<string, string>; mode?: string; }): void; /** Momoa documents */ sources: InputSource[]; } //#endregion //#region src/build/index.d.ts interface BuildRunnerOptions { sources: { filename?: URL; src: string; document: DocumentNode; }[]; config: ConfigInit; logger?: Logger; } declare const SINGLE_VALUE = "SINGLE_VALUE"; declare const MULTI_VALUE = "MULTI_VALUE"; /** Run build stage */ declare function build(tokens: Record<string, TokenNormalized$1>, { sources, logger, config }: BuildRunnerOptions): Promise<BuildRunnerResult>; //#endregion //#region src/config.d.ts /** * Validate and normalize a config */ declare function defineConfig(rawConfig: Config, { logger, cwd }?: ConfigOptions): ConfigInit; /** Merge configs */ declare function mergeConfigs(a: Config, b: Config): Config; //#endregion //#region src/lint/index.d.ts interface LintRunnerOptions { tokens: Record<string, TokenNormalized$1>; filename?: URL; config: ConfigInit; src: string; logger: Logger; } declare function lintRunner({ tokens, filename, config, src, logger }: LintRunnerOptions): Promise<void>; //#endregion //#region src/parse/normalize.d.ts declare const FONT_WEIGHT_MAP: { thin: number; hairline: number; 'extra-light': number; 'ultra-light': number; light: number; normal: number; regular: number; book: number; medium: number; 'semi-bold': number; 'demi-bold': number; bold: number; 'extra-bold': number; 'ultra-bold': number; black: number; heavy: number; 'extra-black': number; 'ultra-black': number; }; /** Fill in defaults, and return predictable shapes for tokens */ declare function normalizeValue<T extends Token$1>(token: T): T['$value']; //#endregion //#region src/parse/validate.d.ts interface ValidateOptions { filename?: URL; src: string; logger: Logger; } interface Visitors { color?: Visitor; dimension?: Visitor; fontFamily?: Visitor; fontWeight?: Visitor; duration?: Visitor; cubicBezier?: Visitor; number?: Visitor; link?: Visitor; boolean?: Visitor; strokeStyle?: Visitor; border?: Visitor; transition?: Visitor; shadow?: Visitor; gradient?: Visitor; typography?: Visitor; root?: Visitor; group?: Visitor; [key: string]: Visitor | undefined; } type Visitor = (json: any, path: string, ast: AnyNode) => any | undefined | null; declare const VALID_COLORSPACES: Set<string>; declare const FONT_WEIGHT_VALUES: Set<string>; declare const STROKE_STYLE_VALUES: Set<string>; declare const STROKE_STYLE_LINE_CAP_VALUES: Set<string>; /** Verify an Alias $value is formatted correctly */ declare function validateAliasSyntax($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Border token is valid */ declare function validateBorder($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Color token is valid */ declare function validateColor($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Cubic Bézier token is valid */ declare function validateCubicBezier($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Dimension token is valid */ declare function validateDimension($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Duration token is valid */ declare function validateDuration($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Font Family token is valid */ declare function validateFontFamily($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Font Weight token is valid */ declare function validateFontWeight($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Gradient token is valid */ declare function validateGradient($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Number token is valid */ declare function validateNumber($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Boolean token is valid */ declare function validateBoolean($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Shadow token’s value is valid */ declare function validateShadowLayer($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Stroke Style token is valid. */ declare function validateStrokeStyle($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** Verify a Transition token is valid */ declare function validateTransition($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions): void; /** * Validate a MemberNode (the entire token object, plus its key in the parent * object) to see if it’s a valid DTCG token or not. Keeping the parent key * really helps in debug messages. */ declare function validateTokenMemberNode(node: MemberNode, { filename, src, logger }: ValidateOptions): void; /** Return whether a MemberNode is a group (has no `$value`). * Groups can have properties that their child nodes will inherit. */ declare function isGroupNode(node: ObjectNode): boolean; /** Check if a token node has the specified property name, and if it does, stores * the value in the `inherited` object as a side effect for future use. If not, * traverses the `inherited` object to find the closest parent that has the property. * * Returns the property value if found locally or in a parent, otherwise undefined. */ declare function computeInheritedProperty(node: MemberNode, propertyName: string, { subpath, inherited }: { subpath: string[]; inherited?: Record<string, MemberNode>; }): MemberNode | undefined; interface ValidateTokenNodeOptions { subpath: string[]; src: string; filename: URL; config: ConfigInit; logger: Logger; parent: AnyNode | undefined; transform?: Visitors; inheritedDeprecatedNode?: MemberNode; inheritedTypeNode?: MemberNode; } /** * Validate does a little more than validate; it also converts to TokenNormalized * and sets up the basic data structure. But aliases are unresolved, and we need * a 2nd normalization pass afterward. */ declare function validateTokenNode(node: MemberNode, { config, filename, logger, parent, inheritedDeprecatedNode, inheritedTypeNode, src, subpath }: ValidateTokenNodeOptions): TokenNormalized$1 | undefined; //#endregion //#region src/parse/alias.d.ts interface ApplyAliasOptions { tokensSet: TokenNormalizedSet$1; filename: URL; src: string; node: ObjectNode; logger: Logger; } type PreAliased<T extends TokenNormalized$1> = { $value: T['$value'] | string; mode: Record<string, T['mode'][string] & { $value: T['$value'] | string; }>; }; //#endregion //#region src/parse/json.d.ts interface JSONVisitor { enter?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void; exit?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void; } declare const CHILD_KEYS: { Document: readonly ["body"]; Object: readonly ["members"]; Member: readonly ["name", "value"]; Element: readonly ["value"]; Array: readonly ["elements"]; String: readonly []; Number: readonly []; Boolean: readonly []; Null: readonly []; Identifier: readonly []; NaN: readonly []; Infinity: readonly []; }; /** Determines if a given value is an AST node. */ declare function isNode(value: unknown): boolean; type ValueNodeWithIndex = ValueNode & { index: number; }; /** Get ObjectNode members as object */ declare function getObjMembers(node: ObjectNode): Record<string | number, ValueNodeWithIndex>; /** Inject members to ObjectNode */ declare function injectObjMembers(node: ObjectNode, members?: MemberNode[]): void; /** Replace an ObjectNode’s contents outright with another */ declare function replaceObjMembers(a: ObjectNode, b: DocumentNode | ObjectNode): void; /** * Variation of Momoa’s traverse(), which keeps track of global path. * Allows mutation of AST (along with any consequences) */ declare function traverse(root: AnyNode, visitor: JSONVisitor): void; /** Determine if an input is likely a JSON string */ declare function maybeRawJSON(input: string): boolean; /** Find Momoa node by traversing paths */ declare function findNode<T = AnyNode>(node: AnyNode, path: string[]): T | undefined; interface ToMomoaOptions { filename?: URL; continueOnError?: boolean; logger: Logger; yamlToMomoa?: typeof ytm; } declare function toMomoa(input: string | Record<string, any>, { continueOnError, filename, logger, yamlToMomoa: ytm }: ToMomoaOptions): InputSource; /** Momoa, just with default options pre-set */ declare function parseJSON(input: string | Record<string, any>, options?: ParseOptions$1): any; //#endregion //#region src/parse/index.d.ts interface ParseOptions { logger?: Logger; config: ConfigInit; /** * Skip lint step * @default false */ skipLint?: boolean; /** * Continue on error? (Useful for `tz check`) * @default false */ continueOnError?: boolean; /** Provide yamlToMomoa module to parse YAML (by default, this isn’t shipped to cut down on package weight) */ yamlToMomoa?: typeof ytm; /** * Transform API * @see https://terrazzo.app/docs/api/js#transform-api */ transform?: Visitors; /** (internal cache; do not use) */ _sources?: Record<string, InputSource>; } interface ParseResult { tokens: Record<string, TokenNormalized$1>; sources: InputSource[]; } /** Parse */ declare function parse(_input: Omit<InputSource, 'document'> | Omit<InputSource, 'document'>[], { logger, skipLint, config, continueOnError, yamlToMomoa, transform, _sources }?: ParseOptions): Promise<ParseResult>; //#endregion export { type AliasToken, type AliasValue, ApplyAliasOptions, type BooleanToken, type BooleanTokenNormalized, type BooleanValue, type BorderToken, type BorderTokenNormalized, type BorderValue, type BorderValueNormalized, BuildEndHookOptions, BuildHookOptions, BuildRunnerOptions, BuildRunnerResult, CHILD_KEYS, type ColorSpace, type ColorToken, type ColorTokenNormalized, type ColorValue, type ColorValueNormalized, Config, ConfigInit, ConfigOptions, type CubicBezierToken, type CubicBezierTokenNormalized, type CubicBezierValue, type CustomTransformOptions, DebugEntry, type DimensionToken, type DimensionTokenNormalized, type DimensionValue, type DurationToken, type DurationTokenNormalized, type DurationValue, FONT_WEIGHT_MAP, FONT_WEIGHT_VALUES, type FontFamilyToken, type FontFamilyTokenNormalized, type FontFamilyValue, type FontFamilyValueNormalized, type FontWeightToken, type FontWeightTokenNormalized, type FontWeightValue, type FontWeightValueNormalized, type GradientStop, type GradientStopNormalized, type GradientToken, type GradientTokenNormalized, type GradientValue, type GradientValueNormalized, type Group, type GroupCore, type GroupOrToken, InputSource, JSONVisitor, LOG_ORDER, type LinkToken, type LinkTokenNormalized, type LinkValue, LintNotice, LintReportDescriptor, LintRule, LintRuleContext, LintRuleLonghand, LintRuleMetaData, LintRuleMetaDataDocs, LintRuleNormalized, LintRuleSeverity, LintRuleShorthand, LintRunnerOptions, LogEntry, LogGroup, LogLevel, LogSeverity, Logger, MULTI_VALUE, type ModeMap, type NumberToken, type NumberTokenNormalized, type NumberValue, OutputFile, OutputFileExpanded, ParseOptions, ParseResult, Plugin, PreAliased, SINGLE_VALUE, STROKE_STYLE_LINE_CAP_VALUES, STROKE_STYLE_VALUES, type ShadowToken, type ShadowTokenNormalized, type ShadowValue, type ShadowValueNormalized, type StringToken, type StringTokenNormalized, type StringValue, type StrokeStyleToken, type StrokeStyleTokenNormalized, type StrokeStyleValue, type StrokeStyleValueExpanded, ToMomoaOptions, type Token, type TokenCore, type TokenMode, type TokenNormalized, type TokenNormalizedCore, type TokenNormalizedSet, TokenTransformed, TokenTransformedMultiValue, TokenTransformedSingleValue, TokensJSONError, type TokensSet, TransformHookOptions, TransformParams, type TransitionToken, type TransitionTokenNormalized, type TransitionValue, type TransitionValueNormalized, type TypographyToken, type TypographyTokenNormalized, type TypographyValue, type TypographyValueNormalized, VALID_COLORSPACES, ValidateOptions, ValidateTokenNodeOptions, ValueNodeWithIndex, Visitor, Visitors, build, computeInheritedProperty, defineConfig, findNode, formatMessage, getObjMembers, injectObjMembers, isGroupNode, isNode, lintRunner, maybeRawJSON, mergeConfigs, normalizeValue as normalize, parse, parseJSON, replaceObjMembers, toMomoa, traverse, validateAliasSyntax, validateBoolean, validateBorder, validateColor, validateCubicBezier, validateDimension, validateDuration, validateFontFamily, validateFontWeight, validateGradient, validateNumber, validateShadowLayer, validateStrokeStyle, validateTokenMemberNode, validateTokenNode, validateTransition }; //# sourceMappingURL=index.d.ts.map