lib/lint-messages.js
function lintFileContents (messages, js) {
  // what was messages imported as
  let importedAtRootAs = false
  let importedMembersAs
  const importedMembersLookup = new Map()

  let namedMatcher, rootMatcher

  // TODO handle importing members, e.g. import { analysis } from messages

  const foundMessages = []

  let lineNumber = 0 // first increment lands at 0

  for (const line of js.split('\n')) {
    lineNumber++
    const parsedImport = parseImportLine(line)
    if (parsedImport) {
      ;[importedAtRootAs, importedMembersAs] = parsedImport
      // make sure we don't catch things like display_messages by making sure there's a diff char before
      rootMatcher = new RegExp(
        `[^a-zA-Z0-9_$]${importedAtRootAs}\\.([^ ,\\(\\)\\[}]+)`,
        'g'
      )
      namedMatcher = new RegExp(
        `[^a-zA-Z0-9_$](${importedMembersAs
          .map(a => a[1])
          .join('|')})\\.([^ ,\\(\\)\\[}]+)`,
        'g'
      )
      importedMembersAs.forEach(([member, importedAs]) =>
        importedMembersLookup.set(importedAs, member)
      )
    } else if (importedAtRootAs || importedMembersAs) {
      let result
      // each subsequent call gets next match
      if (importedAtRootAs) {
        while ((result = rootMatcher.exec(line)) != null) {
          foundMessages.push([result[1], lineNumber])
        }
      }

      // do the same for imported members
      if (importedMembersAs.length > 0) {
        while ((result = namedMatcher.exec(line)) != null) {
          // map back to the names in the messages file
          foundMessages.push([
            `${importedMembersLookup.get(result[1])}.${result[2]}`,
            lineNumber
          ])
        }
      }
    }
  }

  // filter to only the missing ones
  return foundMessages.filter(([message, lineNumber]) => {
    let current = messages
    for (const sub of message.split('.')) {
      current = current[sub]
      if (current == null) {
        return true // something in the chain is undefined
      }
    }

    return false
  })
}
function lintFileContents (messages, js) {
  // what was messages imported as
  let importedAtRootAs = false
  let importedMembersAs
  const importedMembersLookup = new Map()

  let namedMatcher, rootMatcher

  // TODO handle importing members, e.g. import { analysis } from messages

  const foundMessages = []

  let lineNumber = 0 // first increment lands at 0

  for (const line of js.split('\n')) {
    lineNumber++
    const parsedImport = parseImportLine(line)
    if (parsedImport) {
      ;[importedAtRootAs, importedMembersAs] = parsedImport
      // make sure we don't catch things like display_messages by making sure there's a diff char before
      rootMatcher = new RegExp(
        `[^a-zA-Z0-9_$]${importedAtRootAs}\\.([^ ,\\(\\)\\[}]+)`,
        'g'
      )
      namedMatcher = new RegExp(
        `[^a-zA-Z0-9_$](${importedMembersAs
          .map(a => a[1])
          .join('|')})\\.([^ ,\\(\\)\\[}]+)`,
        'g'
      )
      importedMembersAs.forEach(([member, importedAs]) =>
        importedMembersLookup.set(importedAs, member)
      )
    } else if (importedAtRootAs || importedMembersAs) {
      let result
      // each subsequent call gets next match
      if (importedAtRootAs) {
        while ((result = rootMatcher.exec(line)) != null) {
          foundMessages.push([result[1], lineNumber])
        }
      }

      // do the same for imported members
      if (importedMembersAs.length > 0) {
        while ((result = namedMatcher.exec(line)) != null) {
          // map back to the names in the messages file
          foundMessages.push([
            `${importedMembersLookup.get(result[1])}.${result[2]}`,
            lineNumber
          ])
        }
      }
    }
  }

  // filter to only the missing ones
  return foundMessages.filter(([message, lineNumber]) => {
    let current = messages
    for (const sub of message.split('.')) {
      current = current[sub]
      if (current == null) {
        return true // something in the chain is undefined
      }
    }

    return false
  })
}
function lintFileContents ( messages , js ) { // what was messages imported as let importedAtRootAs = false let importedMembersAs const importedMembersLookup = new Map ( ) let namedMatcher , rootMatcher // TODO handle importing members, e.g. import { analysis } from messages const foundMessages = [ ] let lineNumber = 0 // first increment lands at 0 for ( const line of js . split ( '\n' ) ) { lineNumber ++ const parsedImport = parseImportLine ( line ) if ( parsedImport ) { ; [ importedAtRootAs , importedMembersAs ] = parsedImport // make sure we don't catch things like display_messages by making sure there's a diff char before rootMatcher = new RegExp ( ` ${ importedAtRootAs } \\ \\ \\ \\ ` , 'g' ) namedMatcher = new RegExp ( ` ${ importedMembersAs . map ( a => a [ 1 ] ) . join ( '|' ) } \\ \\ \\ \\ ` , 'g' ) importedMembersAs . forEach ( ( [ member , importedAs ] ) => importedMembersLookup . set ( importedAs , member ) ) } else if ( importedAtRootAs || importedMembersAs ) { let result // each subsequent call gets next match if ( importedAtRootAs ) { while ( ( result = rootMatcher . exec ( line ) ) != null ) { foundMessages . push ( [ result [ 1 ] , lineNumber ] ) } } // do the same for imported members if ( importedMembersAs . length > 0 ) { while ( ( result = namedMatcher . exec ( line ) ) != null ) { // map back to the names in the messages file foundMessages . push ( [ ` ${ importedMembersLookup . get ( result [ 1 ] ) } ${ result [ 2 ] } ` , lineNumber ] ) } } } } // filter to only the missing ones return foundMessages . filter ( ( [ message , lineNumber ] ) => { let current = messages for ( const sub of message . split ( '.' ) ) { current = current [ sub ] if ( current == null ) { return true // something in the chain is undefined } } return false } ) }
Lint a file against messages. Exported for testing
Lint a file against messages . Exported for testing