@shopify/theme-language-server-common
Version: 
<h1 align="center" style="position: relative;" > <br> <img src="https://github.com/Shopify/theme-check-vscode/blob/main/images/shopify_glyph.png?raw=true" alt="logo" width="141" height="160"> <br> Theme Language Server </h1>
72 lines (63 loc) • 2.67 kB
text/typescript
import { nodeAtPath } from '@shopify/theme-check-common';
import { JSONPath, MarkedString } from 'vscode-json-languageservice';
import { extractParams, paramsString } from '../../../translations';
import { isJSONRequestContext, JSONRequestContext, RequestContext } from '../../RequestContext';
import { fileMatch } from '../../utils';
import { JSONHoverProvider } from '../JSONHoverProvider';
export class TranslationPathHoverProvider implements JSONHoverProvider {
  private filePatterns = [/^.*\/locales\/[^\/]*\.json$/];
  canHover(context: RequestContext, path: JSONPath): context is JSONRequestContext {
    return (
      fileMatch(context.doc.uri, this.filePatterns) &&
      path.length > 0 &&
      isJSONRequestContext(context)
    );
  }
  async hover(context: RequestContext, path: JSONPath): Promise<MarkedString[]> {
    // Redundant use for type assertion
    if (!this.canHover(context, path)) return [];
    const { doc } = context;
    const ast = doc.ast;
    const node = nodeAtPath(ast, path);
    switch (true) {
      // Because the JSON language service doesn't support composition of hover info,
      // We have to hardcode the docs for the translation file schema here.
      case ['zero', 'one', 'two', 'few', 'many', 'other'].includes(path.at(-1) as string): {
        if (!node || node.type !== 'Literal' || typeof node.value !== 'string') {
          return [`Pluralized translations should have a string value`];
        }
        return [contextualizedLabel(doc.uri, path.slice(0, -1), node.value)];
      }
      case path.at(-1)!.toString().endsWith('_html'): {
        if (!node || node.type !== 'Literal' || typeof node.value !== 'string') {
          return [`Translations ending in '_html' should have a string value`];
        }
        return [
          contextualizedLabel(doc.uri, path, node.value),
          `The '_html' suffix prevents the HTML content from being escaped.`,
        ];
      }
      default: {
        if (!node || node.type !== 'Literal' || typeof node.value !== 'string') {
          return [`Translation group: ${path.join('.')}`];
        }
        return [contextualizedLabel(doc.uri, path, node.value)];
      }
    }
  }
}
export function contextualizedLabel(
  uri: string,
  str: (string | number)[],
  value: string,
): MarkedString {
  if (uri.includes('.schema')) {
    return marked(`"t:${str.join('.')}"`, 'json');
  } else {
    const params = extractParams(value);
    return marked(`{{ '${str.join('.')}' | t${paramsString(params)} }}`, 'liquid');
  }
}
function marked(value: string, language = 'liquid'): { language: string; value: string } {
  return { language, value };
}