Skip to content
Snippets Groups Projects
Select Git revision
  • 3de1b6e9baba7852b6a073252923ef09e641c600
  • master default
2 results

data-binary-source.ts

Blame
  • common.ts 6.26 KiB
    import { lexer, parser, query as q } from 'good-enough-parser';
    import { clone } from '../../../../util/clone';
    import { regEx } from '../../../../util/regex';
    import type { Ctx, NonEmptyArray, PackageVariables } from '../types';
    
    export const REGISTRY_URLS = {
      google: 'https://dl.google.com/android/maven2/',
      gradlePluginPortal: 'https://plugins.gradle.org/m2/',
      jcenter: 'https://jcenter.bintray.com/',
      mavenCentral: 'https://repo.maven.apache.org/maven2',
    };
    
    export const GRADLE_PLUGINS = {
      checkstyle: 'com.puppycrawl.tools:checkstyle',
      codenarc: 'org.codenarc:CodeNarc',
      detekt: 'io.gitlab.arturbosch.detekt:detekt-core',
      findbugs: 'com.google.code.findbugs:findbugs',
      googleJavaFormat: 'com.google.googlejavaformat:google-java-format',
      jacoco: 'org.jacoco:jacoco',
      lombok: 'org.projectlombok:lombok',
      pmd: 'net.sourceforge.pmd:pmd-java',
      spotbugs: 'com.github.spotbugs:spotbugs',
    };
    
    export const ANNOYING_METHODS: ReadonlySet<string> = new Set([
      'createXmlValueRemover',
      'events',
      'args',
      'arrayOf',
      'listOf',
      'mutableListOf',
      'setOf',
      'mutableSetOf',
      'stages', // https://github.com/ajoberstar/reckon,
      'mapScalar', // https://github.com/apollographql/apollo-kotlin
    ]);
    
    export function storeVarToken(ctx: Ctx, node: lexer.Token): Ctx {
      ctx.varTokens.push(node);
      return ctx;
    }
    
    export function increaseNestingDepth(ctx: Ctx): Ctx {
      ctx.tmpNestingDepth.push(...ctx.varTokens);
      ctx.varTokens = [];
      return ctx;
    }
    
    export function reduceNestingDepth(ctx: Ctx): Ctx {
      ctx.tmpNestingDepth.pop();
      return ctx;
    }
    
    export function prependNestingDepth(ctx: Ctx): Ctx {
      ctx.varTokens = [...clone(ctx.tmpNestingDepth), ...ctx.varTokens];
      return ctx;
    }
    
    export function storeInTokenMap(ctx: Ctx, tokenMapKey: string): Ctx {
      ctx.tokenMap[tokenMapKey] = ctx.varTokens;
      ctx.varTokens = [];
    
      return ctx;
    }
    
    export function loadFromTokenMap(
      ctx: Ctx,
      tokenMapKey: string
    ): NonEmptyArray<lexer.Token> {
      const tokens = ctx.tokenMap[tokenMapKey];
      if (!tokens) {
        throw new Error(`Expected token ${tokenMapKey} not found`);
      }
    
      return tokens as NonEmptyArray<lexer.Token>;
    }
    
    export function cleanupTempVars(ctx: Ctx): Ctx {
      ctx.tokenMap = {};
      ctx.varTokens = [];
    
      return ctx;
    }
    
    export function stripReservedPrefixFromKeyTokens(ctx: Ctx): Ctx {
      const unwantedPrefixes = ['ext', 'extra', 'project', 'rootProject'];
      while (
        ctx.varTokens.length > 1 && // ensures there will be always at least one token
        ctx.varTokens[0] &&
        unwantedPrefixes.includes(ctx.varTokens[0].value)
      ) {
        ctx.varTokens.shift();
      }
    
      return ctx;
    }
    
    export function coalesceVariable(ctx: Ctx): Ctx {
      if (ctx.varTokens.length > 1) {
        ctx.varTokens[0]!.value = ctx.varTokens
          .map((token) => token.value)
          .join('.');
        ctx.varTokens.length = 1;
      }
    
      return ctx;
    }
    
    export function interpolateString(
      childTokens: lexer.Token[],
      variables: PackageVariables
    ): string | null {
      const resolvedSubstrings: string[] = [];
      for (const childToken of childTokens) {
        const type = childToken.type;
        if (type === 'string-value') {
          resolvedSubstrings.push(childToken.value);
        } else if (type === 'symbol') {
          const varData = variables[childToken.value];
          if (varData) {
            resolvedSubstrings.push(varData.value);
          } else {
            return null;
          }
        } else {
          return null;
        }
      }
    
      return resolvedSubstrings.join('');
    }
    
    export const qStringValue = q.str((ctx: Ctx, node: lexer.Token) => {
      storeVarToken(ctx, node);
      return ctx;
    });
    
    export const qStringValueAsSymbol = q.str((ctx: Ctx, node: lexer.Token) => {
      const nodeTransformed: lexer.SymbolToken = {
        ...node,
        type: 'symbol',
      };
      storeVarToken(ctx, nodeTransformed);
      return ctx;
    });
    
    // foo.bar["baz"] = "1.2.3"
    export const qVariableAssignmentIdentifier = q
      .sym(storeVarToken)
      .many(
        q.alt(
          q.op<Ctx>('.').sym(storeVarToken),
          q.tree<Ctx>({
            type: 'wrapped-tree',
            maxDepth: 1,
            startsWith: '[',
            endsWith: ']',
            search: q.begin<Ctx>().join(qStringValueAsSymbol).end(),
          })
        ),
        0,
        32
      )
      .handler(stripReservedPrefixFromKeyTokens);
    
    // foo.bar["baz"] -> "foo.bar.baz"
    export const qVariableAccessIdentifier = q
      .handler<Ctx>((ctx) => {
        ctx.tmpTokenStore.backupVarAccessTokens = ctx.varTokens;
        ctx.varTokens = [];
        return ctx;
      })
      .join(qVariableAssignmentIdentifier)
      .handler(coalesceVariable)
      .handler((ctx) => {
        ctx.varTokens = [
          ...ctx.tmpTokenStore.backupVarAccessTokens!,
          ...ctx.varTokens,
        ];
        delete ctx.tmpTokenStore.backupVarAccessTokens;
        return ctx;
      });
    
    // project.ext.getProperty(...)
    // extra.get(...)
    export const qPropertyAccessIdentifier = q
      .opt(q.sym<Ctx>(regEx(/^(?:rootProject|project)$/)).op('.'))
      .alt(
        q.opt(q.sym<Ctx>('ext').op('.')).sym(regEx(/^(?:property|getProperty)$/)),
        q
          .sym<Ctx>(regEx(/^(?:extra|ext)$/))
          .op('.')
          .sym('get')
      )
      .tree({
        maxDepth: 1,
        startsWith: '(',
        endsWith: ')',
        search: q.begin<Ctx>().join(qStringValueAsSymbol).end(),
      })
      .opt(q.sym<Ctx>('as').sym('String'));
    
    // "foo${bar}baz"
    export const qTemplateString = q
      .tree({
        type: 'string-tree',
        maxDepth: 2,
        preHandler: (ctx) => {
          ctx.tmpTokenStore.templateTokens = [];
          return ctx;
        },
        search: q.alt(
          qStringValue.handler((ctx) => {
            ctx.tmpTokenStore.templateTokens?.push(...ctx.varTokens);
            ctx.varTokens = [];
            return ctx;
          }),
          qPropertyAccessIdentifier.handler((ctx) => {
            ctx.tmpTokenStore.templateTokens?.push(...ctx.varTokens);
            ctx.varTokens = [];
            return ctx;
          }),
          qVariableAccessIdentifier.handler((ctx) => {
            ctx.tmpTokenStore.templateTokens?.push(...ctx.varTokens);
            ctx.varTokens = [];
            return ctx;
          })
        ),
      })
      .handler((ctx) => {
        ctx.varTokens = ctx.tmpTokenStore.templateTokens!;
        return ctx;
      });
    
    // foo = "bar"
    // foo + foo + "${foo}" + "foo" => "barbarbarfoo"
    export const qConcatExpr = (
      ...matchers: q.QueryBuilder<Ctx, parser.Node>[]
    ): q.QueryBuilder<Ctx, parser.Node> =>
      q.alt(...matchers).many(q.op<Ctx>('+').alt(...matchers), 0, 32);
    
    export const qValueMatcher = qConcatExpr(
      qTemplateString,
      qPropertyAccessIdentifier,
      qVariableAccessIdentifier
    );