Giter Site home page Giter Site logo

trialanderrororg / parsers Goto Github PK

View Code? Open in Web Editor NEW
13.0 2.0 0.0 99.77 MB

Monorepo for a suite of `unified`-compatible converters for converting between, from, and to .docx, JATS XML, LaTeX, and PDF

Home Page: https://convert.centeroftrialanderror.com

License: GNU General Public License v3.0

JavaScript 3.01% TypeScript 58.52% Ruby 0.07% TeX 33.42% Nearley 0.69% HTML 3.97% CSS 0.31% Shell 0.01%
unified academic-publishing ast docx conversion jats-xml latex open-science nx typescript

parsers's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

parsers's Issues

[ooxast-util-to-jast] Figure out how to properly nest environments and commands

[ooxast-util-to-jast] Figure out how to properly nest environments and commands

@ts-ignore

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/handlers/sec.ts#L45

import { isElement, Title } from 'ooxast'
import { CommandArg, CommandArgOpt, EnvironmentContent } from 'jast'
import { all } from '../all'
import { J, Parents, Name, Node } from '../types'
import { wrap } from '../util/wrap'
import { wrapChildren } from '../util/wrap-children'
import { wrapCommandArg } from '../util/wrap-command-arg'

export const sectionDepth = [
  'part',
  'chapter',
  'section',
  'subsection',
  'subsubsection',
  'paragraph',
  'subparagraph',
  'textbf',
]
export function sec(j: J, node: Parents) {
  let titleElement: Title | null = null

  const sectionArg = sectionDepth[(j.sectionDepth + 1) % sectionDepth.length]

  for (let i = 0; i < node?.children?.length || 0; i++) {
    const child = node?.children[i]
    if (isElement(child) && child.name === 'title') {
      node.children.splice(i, 1)
      titleElement = child
      break
    }
  }

  j.sectionDepth++
  const contents = all(j, node)
  j.sectionDepth--
  if (!titleElement) return contents

  contents.unshift({
    type: 'command',
    name: sectionArg,
    children: [
      {
        type: 'commandArg',
        optional: false,
        // TODO: [ooxast-util-to-jast] Figure out how to properly nest environments and commands
        // @ts-ignore
        children: all(j, titleElement),
      },
    ],
  })

  return wrap(contents)
}

ca45b61807b713a810c7a2851f1b731986316ba7

DRY - try to parse key

DRY - try to parse key

#--
# BibTeX-Ruby
# Copyright (C) 2010-2015 Sylvester Keil <http://sylvester.keil.or.at>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#++

require 'strscan'

module BibTeX
  #
  # The BibTeX::Lexer handles the lexical analysis of BibTeX bibliographies.
  #
  class Lexer
    extend Forwardable

    attr_reader :options, :stack, :mode, :scanner
    attr_writer :mode

    def_delegator :@scanner, :string, :data

    @defaults = {
      include: [:errors],
      strict: true,
      allow_missing_keys: false,
      strip: true
    }

    # Patterns Cache (#37: MacRuby does not cache regular expressions)
    @patterns = {
      space: /[\s]+/o,
      lbrace: /\s*\{/o,
      rbrace: /\s*\}\s*/o,
      braces: /\{|\}/o,
      eq: /\s*=\s*/o,
      comma: /\s*,\s*/o,
      number: /[[:digit:]]+/o,
      name: %r{[[:alpha:][:digit:]/:_!$\?\.%+&\*-]+}io,
      quote: /\s*"/o,
      unquote: /[\{\}"]/o,
      sharp: /\s*#\s*/o,
      object: /@/o,
      period: /./o,
      strict_next: /@[\t ]*/o,
      next: /(^|\n)[\t ]*@[\t ]*/o,
      entry: /[a-z\d:_!\.$%&*-]+/io,
      string: /string/io,
      comment: /comment\b/io,
      preamble: /preamble\b/io,
      key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-]+,}io,
      optional_key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-]*,}io
    }

    MODE = Hash.new(:meta).merge(
      bibtex: :bibtex, entry: :bibtex,
      string: :bibtex, preamble: :bibtex,
      comment: :bibtex,  meta: :meta,
      literal: :literal, content: :content
    ).freeze

    class << self
      attr_reader :defaults, :patterns
    end

    #
    # Creates a new instance. Possible options and their respective
    # default values are:
    #
    # - :include => [:errors] A list that may contain :meta_content, and
    #   :errors; depending on whether or not these are present, the respective
    #   tokens are included in the parse tree.
    # - :strict => true In strict mode objects can start anywhere; therefore
    #   the `@' symbol is not possible except inside literals or @comment
    #   objects; for a more lenient lexer set to false and objects are
    #   expected to start after a new line (leading white space is permitted).
    # - :strip => true When enabled, newlines will be stripped from quoted
    #   string values.
    #
    def initialize(options = {})
      @options = Lexer.defaults.merge(options)
      reset
    end

    def reset
      @stack = []
      @brace_level = 0
      @mode = :meta
      @active_object = nil

      # cache options for speed
      @include_meta_content = @options[:include].include?(:meta_content)
      @include_errors = @options[:include].include?(:errors)

      self
    end

    # Sets the source for the lexical analysis and resets the internal state.
    def data=(data)
      @scanner = StringScanner.new(data)
      reset
    end

    def symbols
      @stack.map(&:first)
    end

    # Returns the next token from the parse stack.
    def next_token
      @stack.shift
    end

    # Returns true if the lexer is currenty parsing a BibTeX object.
    def bibtex_mode?
      MODE[@mode] == :bibtex
    end

    %i[meta literal content].each do |m|
      define_method("#{m}_mode?") { @mode == m }
    end

    # Returns true if the lexer is currently parsing the given object type.
    def active?(object)
      @active_object == object
    end

    # Returns true if the lexer is currently in strict mode.
    def strict?
      !!@options[:strict]
    end

    def allow_missing_keys?
      !!@options[:allow_missing_keys]
    end

    def strip_line_breaks?
      !!options[:strip] && !active?(:comment)
    end

    # Pushes a value onto the parse stack. Returns the Lexer.
    def push(value)
      case value[0]
      when :CONTENT, :STRING_LITERAL
        value[1].gsub!(/\n\s*/, ' ') if strip_line_breaks?

        if !@stack.empty? && value[0] == @stack[-1][0]
          @stack[-1][1] << value[1]
        else
          @stack.push(value)
        end
      when :ERROR
        @stack.push(value) if @include_errors
        leave_object
      when :META_CONTENT
        @stack.push(value) if @include_meta_content
      else
        @stack.push(value)
      end

      self
    end

    # Start the lexical analysis.
    def analyse(string = nil)
      raise(ArgumentError, 'Lexer: failed to start analysis: no source given!') unless
        string || @scanner

      self.data = string || @scanner.string

      send("parse_#{MODE[@mode]}") until @scanner.eos?

      push([false, '$end'])
    end

    private

    def parse_bibtex
      case
      when @scanner.scan(Lexer.patterns[:lbrace])
        @brace_level += 1
        push([:LBRACE, '{'])
        @mode = :content if @brace_level > 1 || @brace_level == 1 && active?(:comment)
      when @scanner.scan(Lexer.patterns[:rbrace])
        @brace_level -= 1
        push([:RBRACE, '}'])
        return leave_object if @brace_level == 0
        return error_unbalanced_braces if @brace_level < 0
      when @scanner.scan(Lexer.patterns[:eq])
        push([:EQ, '='])
      when @scanner.scan(Lexer.patterns[:comma])
        push([:COMMA, ','])
      when @scanner.scan(Lexer.patterns[:number])
        push([:NUMBER, @scanner.matched])
      when @scanner.scan(Lexer.patterns[:name])
        push([:NAME, @scanner.matched.rstrip])
      when @scanner.scan(Lexer.patterns[:quote])
        @mode = :literal
      when @scanner.scan(Lexer.patterns[:sharp])
        push([:SHARP, '#'])
      when @scanner.scan(Lexer.patterns[:object])
        enter_object
      when @scanner.scan(Lexer.patterns[:space])
        # skip
      when @scanner.scan(Lexer.patterns[:period])
        error_unexpected_token
      end
    end

    def parse_meta
      match = @scanner.scan_until(Lexer.patterns[strict? ? :strict_next : :next])
      if @scanner.matched
        push([:META_CONTENT, match.chop])
        enter_object
      else
        push([:META_CONTENT, @scanner.rest])
        @scanner.terminate
      end
    end

    def parse_content
      match = @scanner.scan_until(Lexer.patterns[:braces])
      case @scanner.matched
      when '{'
        @brace_level += 1
        push([:CONTENT, match])
      when '}'
        @brace_level -= 1
        if @brace_level == 0
          push([:CONTENT, match.chop])
          push([:RBRACE, '}'])
          leave_object
        elsif @brace_level == 1 && !active?(:comment)
          push([:CONTENT, match.chop])
          push([:RBRACE, '}'])
          @mode = :bibtex
        elsif @brace_level < 0
          push([:CONTENT, match.chop])
          error_unbalanced_braces
        else
          push([:CONTENT, match])
        end
      else
        push([:CONTENT, @scanner.rest])
        @scanner.terminate
        error_unterminated_content
      end
    end

    def parse_literal
      match = @scanner.scan_until(Lexer.patterns[:unquote])
      case @scanner.matched
      when '{'
        @brace_level += 1
        push([:STRING_LITERAL, match])
      when '}'
        @brace_level -= 1
        if @brace_level < 1
          push([:STRING_LITERAL, match.chop])
          error_unbalanced_braces
        else
          push([:STRING_LITERAL, match])
        end
      when '"'
        if @brace_level == 1
          push([:STRING_LITERAL, match.chop])
          @mode = :bibtex
        else
          push([:STRING_LITERAL, match])
        end
      else
        push([:STRING_LITERAL, @scanner.rest])
        @scanner.terminate
        error_unterminated_string
      end
    end

    # Called when the lexer encounters a new BibTeX object.
    def enter_object
      @brace_level = 0
      push [:AT, '@']

      if @scanner.scan(Lexer.patterns[:string])
        @mode = @active_object = :string
        push [:STRING, @scanner.matched]
      elsif @scanner.scan(Lexer.patterns[:preamble])
        @mode = @active_object = :preamble
        push [:PREAMBLE, @scanner.matched]
      elsif @scanner.scan(Lexer.patterns[:comment])
        @mode = @active_object = :comment
        push [:COMMENT, @scanner.matched]
      elsif @scanner.scan(Lexer.patterns[:entry])
        @mode = @active_object = :entry
        push [:NAME, @scanner.matched]

        # TODO: DRY - try to parse key
        if @scanner.scan(Lexer.patterns[:lbrace])
          @brace_level += 1
          push([:LBRACE, '{'])
          @mode = :content if @brace_level > 1 || @brace_level == 1 && active?(:comment)

          push [:KEY, @scanner.matched.chop.strip] if @scanner.scan(Lexer.patterns[allow_missing_keys? ? :optional_key : :key])
        end

      else
        error_unexpected_object
      end
    end

    # Called when parser leaves a BibTeX object.
    def leave_object
      @mode = :meta
      @active_object = nil
      @brace_level = 0
    end

    def error_unbalanced_braces
      BibTeX.log.warn("Lexer: unbalanced braces at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
      backtrace [:E_UNBALANCED, @scanner.matched]
    end

    def error_unterminated_string
      BibTeX.log.warn("Lexer: unterminated string at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
      backtrace [:E_UNTERMINATED_STRING, @scanner.matched]
    end

    def error_unterminated_content
      BibTeX.log.warn("Lexer: unterminated content at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
      backtrace [:E_UNTERMINATED_CONTENT, @scanner.matched]
    end

    def error_unexpected_token
      BibTeX.log.warn("Lexer: unexpected token `#{@scanner.matched}' at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
      backtrace [:E_UNEXPECTED_TOKEN, @scanner.matched]
    end

    def error_unexpected_object
      BibTeX.log.warn("Lexer: unexpected object at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
      backtrace [:E_UNEXPECTED_OBJECT, '@']
    end

    def backtrace(error)
      bt = []
      bt.unshift(@stack.pop) until @stack.empty? || (!bt.empty? && %i[AT META_CONTENT].include?(bt[0][0]))
      bt << error
      push [:ERROR, bt]
    end
  end
end

e16c3243cc3f8f08833d245d30b1215451b52291

[csl-to-jts] do something with abstract maybe

[csl-to-jts] do something with abstract maybe

https://github.com/JournalOfTrialAndError/JOTE/blob/4b14de45df7a9618fe579a3052560971317727ef/libs/rejour/csl-to-jast/src/lib/csl-to-jast.ts#L168

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  ElementCitation,
  Publisher,
  PubId,
  PersonGroup,
  Source,
  Date,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jjast'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

// @ts-ignore
export function cslToFront(data: CSL): Front {}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-ignore no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-ignore
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? //@ts-ignore
      x('personGroup', { personGroupType: 'author' }, names)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

3997a50e61fa905989564bc7c165bf9b86ffd189

[rejour-relatex]: texastcontenttype is not always assignable as a child of comma...

[rejour-relatex]: texastcontenttype is not always assignable as a child of commandArg

@ts-expect-error texastcontenttype is not always assignable as a child of commandArg

children: fnContent, //fnContent.type === 'paragraph' ? fnContent.children : [fnContent],

// TODO: [rejour-relatex]: texastcontenttype is not always assignable as a child of commandArg

      ])
    }
    case 'fn': {
      const fnContent =
        j.footnotes[
          // prettier-ignore
          // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
          // @ts-expect-error it is text, it has value
          (parseInt(node.children?.[0]?.value?.replace(/[[\]]/g, '')) - 1).toString()
        ]
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          // TODO: [rejour-relatex]: texastcontenttype is not always assignable as a child of commandArg
          // @ts-expect-error texastcontenttype is not always assignable as a child of commandArg
          children: fnContent, //fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }

decd3690ac73b6495fcaaf969827ad76e9938d1f

Figure and Tables

Figure and Tables

https://github.com/JournalOfTrialAndError/JOTE/blob/6acd2f8a12753670c39e9ebbe0eee83c4b19d22b/libs/relatex/src/lib/relatex.ts#L170

import {
  Node as UnistNode,
  Literal as UnistLiteral,
  Parent as UnistParent,
} from 'unist'

export type LtastContent =
  | TopLevelDocumentContent
  | PreambleContent
  | AlignmentContent
  | TabularContent
  | MathContent
  | CommandContent
  | ParagraphContent

export type TopLevelDocumentContent = MathContent | Command
export type PreambleContent = Command | Comment

export type AlignmentContent = MathEnvironmentAligned | TabularContent

export type TabularContent =
  | AlignmentTab
  | InlineMath
  | Command
  | Linebreak
  | Text

export type MathContent =
  | AlignmentTab
  | MathCharacter
  | MathEnvironmentAligned
  | Script

export type CommandContent = Command | Text

export type ParagraphContent = Text | InlineMath | Command | Comment

export type NeedsEscape = '&'
export interface Root extends Parent {
  type: 'root'
  children: TopLevelDocumentContent[] | (Preamble | DocumentEnvironment)[]
}
export interface Preamble extends Parent {
  type: 'preamble'
  content: PreambleContent[]
}
export interface DocumentEnvironment extends Parent {
  type: 'document'
  content: TopLevelDocumentContent[]
}

// export interface NodeMap<TNode extends UtensilNode = UtensilNode>
//   extends UnistNode {
//   type: Pick<TNode, 'kind'> extends string ? Pick<TNode, 'kind'> : string
//   position?: Position
//   // children?: Pick<TNode, 'content'>
// }

export interface Parent extends UnistParent {
  children: LtastContent[]
}

export interface Group<Children extends LtastContent = LtastContent>
  extends Parent {
  type: 'group'
  children: Children[]
}
export interface Command extends Parent {
  type: 'command'
  name: string
  children: CommandArg[]
}
export interface CommandArg extends Group<CommandContent> {
  optional?: boolean
}

export interface Environment<TNode extends UnistNode = UnistNode>
  extends Parent {
  type: 'environment'
  name: string
  children: Array<CommandArg | TNode>
}
export type MatrixNames =
  | 'array'
  | 'matrix'
  | 'pmatrix'
  | 'vmatrix'
  | 'Vmatrix'
  | 'bmatrix'
  | 'Bmatrix'
export interface MathEnvironment extends Environment<MathContent> {
  type: 'environment'
  name:
    | 'equation'
    | 'equation*'
    | 'align'
    | 'align*'
    | 'math'
    | 'displaymath'
    | 'aligned'
    | MatrixNames
}
export interface MathEnvironmentAligned extends MathEnvironment {
  name: 'aligned'
}
export interface MathContainer extends Parent {
  type: 'mathContainer'
  children: MathContent[]
}
export interface InlineMath extends MathContainer {
  name: 'inlineMath'
  delimiters?: 'dollar' | 'parenthesis'
}
export interface DisplayMath extends MathContainer {
  name: 'displayMath'
  delimiters?: 'dollar' | 'bracket'
}
export interface Script extends Parent {
  type: 'super' | 'sub'
  children: MathContent[]
}

export interface MathCharacter extends UnistLiteral {
  type: 'math'
  value: string
}

export interface AlignmentTab extends UnistLiteral {
  type: 'alignmentTab'
  value: '&'
}

export interface Paragraph extends Parent {
  type: 'paragraph'
  children: ParagraphContent[]
}
export interface SoftBreak extends UnistLiteral {
  type: 'break'
  value: '\\n'
}

export interface Comment extends UnistLiteral {
  type: 'comment'
  value: string
}

export interface Text extends UnistLiteral {
  type: 'text'
}

export interface Linebreak extends UnistNode {
  height?: string
}
// export type Nodes = NodeMap<UtensilNodes>
// export interface ParentMap<TNode extends UtensilParent = UtensilParent>
//   extends NodeMap<TNode>,
//     UnistParent {
//   children: []
// }

// export type Parents = Parent<UtensilParents>

// export type Literal<TNode extends UtensilLiteral = UtensilLiteral> =
//   Node<TNode> &
//     UnistLiteral & {
//       value: string
//     }

//export type Literals = Literal<UtensilLiterals>
// TODO: Figure and Tables

export interface Figure extends Environment {
  name: 'figure'
  float?: boolean
}
export interface Table extends Environment {
  name: 'table'
  float?: boolean
}
export interface Tabular extends Environment<TabularContent> {
  name: 'tabular'
  package?: 'plain' | 'tabularx' | 'tabulary' | 'tabu'
}

export { UnistNode as Node, UnistLiteral as Literal }

5a1449817b087a4e65c34fdc1baf9e025c545245

[csl-to-jts] use citation key if available

[csl-to-jts] use citation key if available

https://github.com/JournalOfTrialAndError/JOTE/blob/4b14de45df7a9618fe579a3052560971317727ef/libs/rejour/csl-to-jast/src/lib/csl-to-jast.ts#L170

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  ElementCitation,
  Publisher,
  PubId,
  PersonGroup,
  Source,
  Date,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jjast'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

// @ts-ignore
export function cslToFront(data: CSL): Front {}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-ignore no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-ignore
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? //@ts-ignore
      x('personGroup', { personGroupType: 'author' }, names)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

c7e2f105809767599915d3b5c4dc18d6c0086e56

[rejour-relatex] make latex cite command setting more modular and customizable

[rejour-relatex] make latex cite command setting more modular and customizable

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-to-texast/src/lib/handlers/xref.ts#L52

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Xref, Text } from 'jast-types'
import { CommandArg } from 'texast'
import { J } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'footnote',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  const labelToText: { [key: string]: string } = {
    page: 'pp.',
    appendix: 'App.',
  }

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      const customType = node.attributes.customType

      // TODO: [rejour-relatex] make latex cite command setting more modular and customizable
      let command
      let pre
      let post
      if (customType) {
        const customData = JSON.parse(customType)
        const { prefix, infix, label, locator, mode, suffix } = customData

        const pref = (mode ? infix : prefix) || ''

        const suff = `${
          label && label !== 'none'
            ? `${labelToText[label] || label || 'pp.'} `
            : ''
        }${locator || ''}`
        command = mode ? 'textcite' : 'parencite'

        if (pref) pre = pref
        if (suff) post = suff
      }

      const optCommandArgs = createOptCiteArgs(pre, post)
      return j(
        node,
        'command',
        { name: command || j.citationAnalyzer(node) || 'autocite' },
        [
          ...optCommandArgs,
          {
            type: 'commandArg',
            children: [
              {
                type: 'text',
                value:
                  node.attributes.rid ||
                  node.children
                    .map((node) => {
                      //@ts-expect-error it is text, it has value
                      const n = node.value.replace(/[[\], ]/g, '')
                      return n ? `bib${n}` : undefined
                    })
                    .filter((n) => !!n)
                    .join(','),
              },
            ],
          },
        ]
      )
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error It is text, it has value
              value: 'bib' + node.children[0]?.value?.replace(/[[\]]/g, ''),
            },
          ],
        },
      ])
    }
    case 'fn': {
      const fnContent = j.footnotes[
        // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
        // @ts-expect-error it is text, it has value
        parseInt(node.children?.[0]?.value?.replace(/[[\]]/g, '')) - 1
      ] as any
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          children:
            fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

function createOptCiteArgs(pre?: string, post?: string) {
  if (!pre && !post) return []
  if (!post) {
    return [
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: '',
          } as Text,
        ],
      } as CommandArg,
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: pre,
          } as Text,
        ],
      } as CommandArg,
    ]
  }

  return [
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: post,
        } as Text,
      ],
    } as CommandArg,
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: pre,
        } as Text,
      ],
    } as CommandArg,
  ]
}

de15e9782e858f27f6193aaf034133f603faedb0

[jast-util-to-texast] Figure out how to properly nest environments and commands

[jast-util-to-texast] Figure out how to properly nest environments and commands

@ts-ignore

https://github.com/JournalOfTrialAndError/JOTE/blob/9dbab5875d891f5e94f9627c3ae5c3a93b743613/libs/rejour/jast-util-to-texast/src/lib/handlers/sec.ts#L45

import { isElement, Title } from 'jast'
import { CommandArg, CommandArgOpt, EnvironmentContent } from 'texast'
import { all } from '../all'
import { J, Parents, TagName, Node } from '../types'
import { wrap } from '../util/wrap'
import { wrapChildren } from '../util/wrap-children'
import { wrapCommandArg } from '../util/wrap-command-arg'

export const sectionDepth = [
  'part',
  'chapter',
  'section',
  'subsection',
  'subsubsection',
  'paragraph',
  'subparagraph',
  'textbf',
]
export function sec(j: J, node: Parents) {
  let titleElement: Title | null = null

  const sectionArg = sectionDepth[(j.sectionDepth + 1) % sectionDepth.length]

  for (let i = 0; i < node?.children?.length || 0; i++) {
    const child = node?.children[i]
    if (isElement(child) && child.tagName === 'title') {
      node.children.splice(i, 1)
      titleElement = child
      break
    }
  }

  j.sectionDepth++
  const contents = all(j, node)
  j.sectionDepth--
  if (!titleElement) return contents

  contents.unshift({
    type: 'command',
    name: sectionArg,
    children: [
      {
        type: 'commandArg',
        optional: false,
        // TODO: [jast-util-to-texast] Figure out how to properly nest environments and commands
        // @ts-ignore
        children: all(j, titleElement),
      },
    ],
  })

  return wrap(contents)
}

7b133727cabb8d4c68e46fda22bbc09e8e95d51a

output sequence score: scs[n] (float)

output sequence score: scs[n] (float)

to take care of not discarding the raw input as we want to send it

back to the output with the additional predicted labels.

available, and label it with Viterbi.

m.label(tokens, options = {}) # => array of labelled tokens

m.label(filename, options = {}) # => array of labelled tokens

// TODO output sequence score: scs[n] (float)


#include <stdio.h>
#include <string.h>

#include "options.h"
#include "reader.h"
#include "decoder.h"
#include "model.h"
#include "trainers.h"
#include "progress.h"
#include "quark.h"
#include "tools.h"
#include "wapiti.h"
#include "native.h"

VALUE mWapiti;
VALUE mNative;
VALUE cOptions;
VALUE cModel;
VALUE cArgumentError;
VALUE cNativeError;
VALUE cLogger;

/* --- Options Class --- */

// Auxiliary Methods

static opt_t *get_options(VALUE self) {
  opt_t *options;
  Data_Get_Struct(self, opt_t, options);
  return options;
}

// Copies a Ruby string to the heap and stores it in a pointer.
// Frees the pointer before assigning the new value.
static void copy_string(char **dst, VALUE rb_string) {
  Check_Type(rb_string, T_STRING);

  if (*dst) { free(*dst); *dst = (char*)0; }
  *dst = calloc(RSTRING_LEN(rb_string) + 1, sizeof(char));

  memcpy(*dst, StringValuePtr(rb_string), RSTRING_LEN(rb_string) + 1);
}

// Moves a string to the heap. We use this to move default
// values to the heap during initialization.
static char *to_heap(const char *string) {
  char* ptr = calloc(strlen(string), sizeof(char));
  memcpy(ptr, string, strlen(string));
  return ptr;
}


// Constructor / Desctructor

static void mark_options(opt_t* options __attribute__((__unused__))) {
  // nothing
}

static void deallocate_options(opt_t* options) {
  // free string options
  if (options->input) { free(options->input); }
  if (options->output) { free(options->output); }
  if (options->algo) { free((void*)options->algo); }
  if (options->type) { free((void*)options->type); }
  if (options->devel) { free(options->devel); }
  if (options->pattern) { free((void*)options->pattern); }

  free(options);
  options = (opt_t*)0;
}

static VALUE allocate_options(VALUE self) {
  opt_t* options = malloc(sizeof(opt_t));
  return Data_Wrap_Struct(self, mark_options, deallocate_options, options);
}

static VALUE initialize_options(int argc, VALUE *argv, VALUE self) {
  opt_t* options = get_options(self);
  *options = opt_defaults;

  if (options->maxiter == 0) {
    options->maxiter = INT_MAX;
  }

  // Copy default algorithm and type name to the heap
  // so that all options strings are on the heap.
  options->algo = to_heap(options->algo);
  options->type = to_heap(options->type);

  if (argc > 1) {
    rb_raise(cArgumentError,
      "wrong number of arguments (%d for 0..1)", argc);
  }

  // set defaults
  if (argc) {
    Check_Type(argv[0], T_HASH);
    (void)rb_funcall(self, rb_intern("update!"), 1, argv[0]);
  }

  // yield self if block_given?
  if (rb_block_given_p()) {
    rb_yield(self);
  }

  return self;
}


// Instance Methods


// Fixnum Accessors

static VALUE options_nbest(VALUE self) {
  return INT2FIX(get_options(self)->nbest);
}

static VALUE options_set_nbest(VALUE self, VALUE rb_fixnum) {
  Check_Type(rb_fixnum, T_FIXNUM);
  get_options(self)->nbest = FIX2INT(rb_fixnum);

  return rb_fixnum;
}


static VALUE options_stopwin(VALUE self) {
  return INT2FIX(get_options(self)->stopwin);
}

static VALUE options_set_stopwin(VALUE self, VALUE rb_fixnum) {
  Check_Type(rb_fixnum, T_FIXNUM);
  get_options(self)->stopwin = FIX2INT(rb_fixnum);

  return rb_fixnum;
}

static VALUE options_objwin(VALUE self) {
  return INT2FIX(get_options(self)->objwin);
}

static VALUE options_set_objwin(VALUE self, VALUE rb_fixnum) {
  Check_Type(rb_fixnum, T_FIXNUM);
  get_options(self)->objwin = FIX2INT(rb_fixnum);

  return rb_fixnum;
}


static VALUE options_maxiter(VALUE self) {
  return INT2FIX(get_options(self)->maxiter);
}

static VALUE options_set_maxiter(VALUE self, VALUE rb_fixnum) {
  opt_t *options = get_options(self);

  Check_Type(rb_fixnum, T_FIXNUM);
  options->maxiter = FIX2INT(rb_fixnum);

  return rb_fixnum;
}

static VALUE options_jobsize(VALUE self) {
  return INT2FIX(get_options(self)->jobsize);
}

static VALUE options_set_jobsize(VALUE self, VALUE rb_fixnum) {
  opt_t *options = get_options(self);

  Check_Type(rb_fixnum, T_FIXNUM);
  options->jobsize = FIX2INT(rb_fixnum);

  return rb_fixnum;
}

static VALUE options_nthread(VALUE self) {
  return INT2FIX(get_options(self)->nthread);
}

static VALUE options_set_nthread(VALUE self, VALUE rb_fixnum) {
  opt_t *options = get_options(self);

  Check_Type(rb_fixnum, T_FIXNUM);
  options->nthread = FIX2INT(rb_fixnum);

  return rb_fixnum;
}

static VALUE options_histsz(VALUE self) {
  return INT2FIX(get_options(self)->lbfgs.histsz);
}

static VALUE options_set_histsz(VALUE self, VALUE rb_fixnum) {
  Check_Type(rb_fixnum, T_FIXNUM);
  get_options(self)->lbfgs.histsz = FIX2INT(rb_fixnum);

  return rb_fixnum;
}

static VALUE options_maxls(VALUE self) {
  return INT2FIX(get_options(self)->lbfgs.maxls);
}

static VALUE options_set_maxls(VALUE self, VALUE rb_fixnum) {
  Check_Type(rb_fixnum, T_FIXNUM);
  get_options(self)->lbfgs.maxls = FIX2INT(rb_fixnum);

  return rb_fixnum;
}


// Float Accessors

static VALUE options_rho1(VALUE self) {
  return rb_float_new(get_options(self)->rho1);
}

static VALUE options_set_rho1(VALUE self, VALUE rb_numeric) {
  get_options(self)->rho1 = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_rho2(VALUE self) {
  return rb_float_new(get_options(self)->rho2);
}

static VALUE options_set_rho2(VALUE self, VALUE rb_numeric) {
  get_options(self)->rho2 = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_stopeps(VALUE self) {
  return rb_float_new(get_options(self)->stopeps);
}

static VALUE options_set_stopeps(VALUE self, VALUE rb_numeric) {
  get_options(self)->stopeps = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_eta0(VALUE self) {
  return rb_float_new(get_options(self)->sgdl1.eta0);
}

static VALUE options_set_eta0(VALUE self, VALUE rb_numeric) {
  get_options(self)->sgdl1.eta0 = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_alpha(VALUE self) {
  return rb_float_new(get_options(self)->sgdl1.alpha);
}

static VALUE options_set_alpha(VALUE self, VALUE rb_numeric) {
  get_options(self)->sgdl1.alpha = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_kappa(VALUE self) {
  return rb_float_new(get_options(self)->bcd.kappa);
}

static VALUE options_set_kappa(VALUE self, VALUE rb_numeric) {
  get_options(self)->bcd.kappa = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_stpmin(VALUE self) {
  return rb_float_new(get_options(self)->rprop.stpmin);
}

static VALUE options_set_stpmin(VALUE self, VALUE rb_numeric) {
  get_options(self)->rprop.stpmin = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_stpmax(VALUE self) {
  return rb_float_new(get_options(self)->rprop.stpmax);
}

static VALUE options_set_stpmax(VALUE self, VALUE rb_numeric) {
  get_options(self)->rprop.stpmax = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_stpinc(VALUE self) {
  return rb_float_new(get_options(self)->rprop.stpinc);
}

static VALUE options_set_stpinc(VALUE self, VALUE rb_numeric) {
  get_options(self)->rprop.stpinc = NUM2DBL(rb_numeric);
  return rb_numeric;
}

static VALUE options_stpdec(VALUE self) {
  return rb_float_new(get_options(self)->rprop.stpdec);
}

static VALUE options_set_stpdec(VALUE self, VALUE rb_numeric) {
  get_options(self)->rprop.stpdec = NUM2DBL(rb_numeric);
  return rb_numeric;
}



// Boolean Accessors

static VALUE options_maxent(VALUE self) {
  return get_options(self)->maxent ? Qtrue : Qfalse;
}

static VALUE options_set_maxent(VALUE self, VALUE rb_boolean) {
  get_options(self)->maxent = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_compact(VALUE self) {
  return get_options(self)->compact ? Qtrue : Qfalse;
}

static VALUE options_set_compact(VALUE self, VALUE rb_boolean) {
  get_options(self)->compact = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_sparse(VALUE self) {
  return get_options(self)->sparse ? Qtrue : Qfalse;
}

static VALUE options_set_sparse(VALUE self, VALUE rb_boolean) {
  get_options(self)->sparse = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_check(VALUE self) {
  return get_options(self)->check ? Qtrue : Qfalse;
}

static VALUE options_set_check(VALUE self, VALUE rb_boolean) {
  get_options(self)->check = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_label(VALUE self) {
  return get_options(self)->label ? Qtrue : Qfalse;
}

static VALUE options_set_label(VALUE self, VALUE rb_boolean) {
  get_options(self)->label = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_outsc(VALUE self) {
  return get_options(self)->outsc ? Qtrue : Qfalse;
}

static VALUE options_set_outsc(VALUE self, VALUE rb_boolean) {
  get_options(self)->outsc = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_lblpost(VALUE self) {
  return get_options(self)->lblpost ? Qtrue : Qfalse;
}

static VALUE options_set_lblpost(VALUE self, VALUE rb_boolean) {
  get_options(self)->lblpost = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_clip(VALUE self) {
  return get_options(self)->lbfgs.clip ? Qtrue : Qfalse;
}

static VALUE options_set_clip(VALUE self, VALUE rb_boolean) {
  get_options(self)->lbfgs.clip = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}

static VALUE options_cutoff(VALUE self) {
  return get_options(self)->rprop.cutoff ? Qtrue : Qfalse;
}

static VALUE options_set_cutoff(VALUE self, VALUE rb_boolean) {
  get_options(self)->rprop.cutoff = !(TYPE(rb_boolean) == T_NIL || !rb_boolean);
  return rb_boolean;
}




// String Accessors

static VALUE options_pattern(VALUE self) {
  const char *pattern = get_options(self)->pattern;
  return rb_str_new2(pattern ? pattern : "");
}

static VALUE options_set_pattern(VALUE self, VALUE rb_string) {
  opt_t *options = get_options(self);
  copy_string((char**)&(options->pattern), rb_string);

  return rb_string;
}

static VALUE options_model(VALUE self) {
  const char *model = get_options(self)->model;
  return rb_str_new2(model ? model : "");
}

static VALUE options_set_model(VALUE self, VALUE rb_string) {
  opt_t *options = get_options(self);
  copy_string(&(options->model), rb_string);
  return rb_string;
}

static VALUE options_algorithm(VALUE self) {
  const char *algorithm = get_options(self)->algo;
  return rb_str_new2(algorithm ? algorithm : "");
}

static VALUE options_set_algorithm(VALUE self, VALUE rb_string) {
  opt_t *options = get_options(self);
  copy_string((char**)&(options->algo), rb_string);
  return rb_string;
}

static VALUE options_type(VALUE self) {
  const char *type = get_options(self)->type;
  return rb_str_new2(type ? type : "");
}

static VALUE options_set_type(VALUE self, VALUE rb_string) {
  opt_t *options = get_options(self);
  copy_string((char**)&(options->type), rb_string);
  return rb_string;
}


void Init_options() {
  cOptions = rb_define_class_under(mWapiti, "Options", rb_cObject);
  rb_define_alloc_func(cOptions, allocate_options);

  rb_define_method(cOptions, "initialize", initialize_options, -1);

  // Option Accessors

  rb_define_method(cOptions, "stopwin", options_stopwin, 0);
  rb_define_method(cOptions, "stopwin=", options_set_stopwin, 1);

  rb_define_alias(cOptions, "stop_window", "stopwin");
  rb_define_alias(cOptions, "stop_window=", "stopwin=");

  rb_define_method(cOptions, "objwin", options_objwin, 0);
  rb_define_method(cOptions, "objwin=", options_set_objwin, 1);

  rb_define_alias(cOptions, "convergence_window", "objwin");
  rb_define_alias(cOptions, "convergence_window=", "objwin=");

  rb_define_method(cOptions, "maxiter", options_maxiter, 0);
  rb_define_method(cOptions, "maxiter=", options_set_maxiter, 1);

  rb_define_alias(cOptions, "max_iterations", "maxiter");
  rb_define_alias(cOptions, "max_iterations=", "maxiter=");

  rb_define_method(cOptions, "jobsize", options_jobsize, 0);
  rb_define_method(cOptions, "jobsize=", options_set_jobsize, 1);

  rb_define_method(cOptions, "nthread", options_nthread, 0);
  rb_define_method(cOptions, "nthread=", options_set_nthread, 1);

  rb_define_alias(cOptions, "threads", "nthread");
  rb_define_alias(cOptions, "threads=", "nthread=");

  rb_define_method(cOptions, "rho1", options_rho1, 0);
  rb_define_method(cOptions, "rho1=", options_set_rho1, 1);

  rb_define_method(cOptions, "rho2", options_rho2, 0);
  rb_define_method(cOptions, "rho2=", options_set_rho2, 1);

  rb_define_method(cOptions, "stopeps", options_stopeps, 0);
  rb_define_method(cOptions, "stopeps=", options_set_stopeps, 1);

  rb_define_alias(cOptions, "stop_epsilon", "stopeps");
  rb_define_alias(cOptions, "stop_epsilon=", "stopeps=");

  rb_define_method(cOptions, "maxent", options_maxent, 0);
  rb_define_method(cOptions, "maxent=", options_set_maxent, 1);

  rb_define_alias(cOptions, "maxent?", "maxent");

  rb_define_method(cOptions, "compact", options_compact, 0);
  rb_define_method(cOptions, "compact=", options_set_compact, 1);

  rb_define_alias(cOptions, "compact?", "compact");

  rb_define_method(cOptions, "sparse", options_sparse, 0);
  rb_define_method(cOptions, "sparse=", options_set_sparse, 1);

  rb_define_alias(cOptions, "sparse?", "sparse");

  rb_define_method(cOptions, "skip_tokens", options_label, 0);
  rb_define_method(cOptions, "skip_tokens=", options_set_label, 1);

  rb_define_alias(cOptions, "skip_tokens?", "skip_tokens");

  rb_define_method(cOptions, "check", options_check, 0);
  rb_define_method(cOptions, "check=", options_set_check, 1);

  rb_define_alias(cOptions, "check?", "check");

  rb_define_method(cOptions, "lblpost", options_lblpost, 0);
  rb_define_method(cOptions, "lblpost=", options_set_lblpost, 1);

  rb_define_alias(cOptions, "lblpost?", "lblpost");

  rb_define_alias(cOptions, "posterior", "lblpost");
  rb_define_alias(cOptions, "posterior?", "lblpost");
  rb_define_alias(cOptions, "posterior=", "lblpost=");

  rb_define_method(cOptions, "outsc", options_outsc, 0);
  rb_define_method(cOptions, "outsc=", options_set_outsc, 1);

  rb_define_alias(cOptions, "outsc?", "outsc");

  rb_define_alias(cOptions, "score", "outsc");
  rb_define_alias(cOptions, "score?", "outsc");
  rb_define_alias(cOptions, "score=", "outsc=");

  rb_define_method(cOptions, "pattern", options_pattern, 0);
  rb_define_method(cOptions, "pattern=", options_set_pattern, 1);

  rb_define_alias(cOptions, "template", "pattern");
  rb_define_alias(cOptions, "template=", "pattern=");

  rb_define_method(cOptions, "model", options_model, 0);
  rb_define_method(cOptions, "model=", options_set_model, 1);

  rb_define_method(cOptions, "algorithm", options_algorithm, 0);
  rb_define_method(cOptions, "algorithm=", options_set_algorithm, 1);

  rb_define_alias(cOptions, "algo", "algorithm");
  rb_define_alias(cOptions, "algo=", "algorithm=");

  rb_define_method(cOptions, "type", options_type, 0);
  rb_define_method(cOptions, "type=", options_set_type, 1);

  rb_define_method(cOptions, "clip", options_clip, 0);
  rb_define_method(cOptions, "clip=", options_set_clip, 1);

  rb_define_method(cOptions, "histsz", options_histsz, 0);
  rb_define_method(cOptions, "histsz=", options_set_histsz, 1);

  rb_define_method(cOptions, "maxls", options_maxls, 0);
  rb_define_method(cOptions, "maxls=", options_set_maxls, 1);

  rb_define_method(cOptions, "eta0", options_eta0, 0);
  rb_define_method(cOptions, "eta0=", options_set_eta0, 1);

  rb_define_method(cOptions, "alpha", options_alpha, 0);
  rb_define_method(cOptions, "alpha=", options_set_alpha, 1);

  rb_define_method(cOptions, "kappa", options_kappa, 0);
  rb_define_method(cOptions, "kappa=", options_set_kappa, 1);

  rb_define_method(cOptions, "stpmin", options_stpmin, 0);
  rb_define_method(cOptions, "stpmin=", options_set_stpmin, 1);

  rb_define_method(cOptions, "stpmax", options_stpmax, 0);
  rb_define_method(cOptions, "stpmax=", options_set_stpmax, 1);

  rb_define_method(cOptions, "stpinc", options_stpinc, 0);
  rb_define_method(cOptions, "stpinc=", options_set_stpinc, 1);

  rb_define_method(cOptions, "stpdec", options_stpdec, 0);
  rb_define_method(cOptions, "stpdec=", options_set_stpdec, 1);

  rb_define_method(cOptions, "cutoff", options_cutoff, 0);
  rb_define_method(cOptions, "cutoff=", options_set_cutoff, 1);

  rb_define_method(cOptions, "nbest", options_nbest, 0);
  rb_define_method(cOptions, "nbest=", options_set_nbest, 1);

}


/* --- Model Class --- */

// Auxiliary Methods

static mdl_t *get_model(VALUE self) {
  mdl_t *model;
  Data_Get_Struct(self, mdl_t, model);
  return model;
}

// Constructor / Desctructor

static void mark_model(mdl_t *model __attribute__((__unused__))) {
  // nothing
}

static void deallocate_model(mdl_t *model) {
  if (model) {
    mdl_free(model);
    model = (mdl_t*)0;
  }
}

static VALUE allocate_model(VALUE self) {
  mdl_t *model = mdl_new(rdr_new(false));
  return Data_Wrap_Struct(self, mark_model, deallocate_model, model);
}

static VALUE model_set_options(VALUE self, VALUE rb_options) {
  if (strncmp("Wapiti::Options", rb_obj_classname(rb_options), 15) != 0) {
    rb_raise(cArgumentError, "argument must be a Wapiti::Options instance");
  }

  mdl_t *model = get_model(self);

  // Store reference to options in model struct
  model->opt = get_options(rb_options);

  // Update reader
  model->reader->autouni = model->opt->maxent;

  // Save instance variable
  rb_ivar_set(self, rb_intern("@options"), rb_options);

  return rb_options;
}

static VALUE initialize_model(int argc, VALUE *argv, VALUE self) {
  VALUE options;

  if (argc > 1) {
    rb_raise(cArgumentError,
      "wrong number of arguments (%d for 0..1)", argc);
  }

  if (argc) {
    if (TYPE(argv[0]) == T_HASH) {
      options = rb_funcall(cOptions, rb_intern("new"), 1, argv[0]);
    } else {
      if (strncmp("Wapiti::Options", rb_obj_classname(argv[0]), 15) != 0) {
        rb_raise(cArgumentError, "argument must be a hash or an options instance");
      }
      options = argv[0];
    }
  } else {
    options = rb_funcall(cOptions, rb_intern("new"), 0);
  }

  // yield options if block_given?
  if (rb_block_given_p()) {
    rb_yield(options);
  }

  model_set_options(self, options);

  // Load a previous model if specified by options
  if (get_options(options)->model) {
    rb_funcall(self, rb_intern("load"), 0);
  }

  // initialize counters
  rb_funcall(self, rb_intern("reset_counters"), 0);

  return self;
}


// Native accessors

static VALUE model_nlbl(VALUE self) {
  return INT2FIX(get_model(self)->nlbl);
}

static VALUE model_nobs(VALUE self) {
  return INT2FIX(get_model(self)->nobs);
}

static VALUE model_nftr(VALUE self) {
  return INT2FIX(get_model(self)->nftr);
}


// Instance methods

static VALUE model_sync(VALUE self) {
  mdl_sync(get_model(self));
  return self;
}

static VALUE model_compact(VALUE self) {
  mdl_compact(get_model(self));
  return self;
}

// call-seq:
//   m.save       # => saves the model to the file defined in m.path
//   m.save(path) # => sets m.path and saves the model to the file <path>
//
// Saves the model to a file. Uses the Model's path if no argument given,
// otherwise uses the passed-in argument as the Model's path.
static VALUE model_save(int argc, VALUE *argv, VALUE self) {
  if (argc > 1) {
    rb_raise(cArgumentError,
      "wrong number of arguments (%d for 0..1)", argc);
  }

  mdl_t *model = get_model(self);

  // save passed-in path in options
  if (argc) {
    Check_Type(argv[0], T_STRING);
    rb_ivar_set(self, rb_intern("@path"), argv[0]);
  }

  // open the output file
  VALUE path = rb_ivar_get(self, rb_intern("@path"));

  if (NIL_P(path)) {
    fatal("failed to save model: no path given");
  }

  FILE *file = ufopen(path, "w");
  mdl_save(model, file);
  fclose(file);

  return self;
}

static VALUE model_load(int argc, VALUE *argv, VALUE self) {
  if (argc > 1) {
    rb_raise(cArgumentError,
      "wrong number of arguments (%d for 0..1)", argc);
  }

  mdl_t *model = get_model(self);

  // save passed-in argument in options
  if (argc) {
    Check_Type(argv[0], T_STRING);
    rb_ivar_set(self, rb_intern("@path"), argv[0]);
  }

  // open the model file
  VALUE path = rb_ivar_get(self, rb_intern("@path"));

  if (NIL_P(path)) {
    fatal("failed to load model: no path given");
  }

  FILE *file = ufopen(path, "r");
  mdl_load(model, file);
  fclose(file);

  return self;
}

static dat_t *to_dat(rdr_t *reader, VALUE data, bool labelled) {
  Check_Type(data, T_ARRAY);

  const unsigned int n = RARRAY_LEN(data);
  unsigned int i, j, k;

  dat_t *dat = wapiti_xmalloc(sizeof(dat_t));
  dat->nseq = 0;
  dat->mlen = 0;
  dat->lbl = labelled;
  dat->seq = wapiti_xmalloc(sizeof(seq_t*) * n);

  for (i = 0; i < n; ++i) {
    VALUE sequence = rb_ary_entry(data, i);
    Check_Type(sequence, T_ARRAY);

    k = RARRAY_LEN(sequence);
    raw_t *raw = wapiti_xmalloc(sizeof(raw_t) + sizeof(char*) * k);

    for (j = 0; j < k; ++j) {
      VALUE line = rb_ary_entry(sequence, j);
      Check_Type(line, T_STRING);
      raw->lines[j] = StringValueCStr(line);
    }

    raw->len = k;

    seq_t *seq = rdr_raw2seq(reader, raw, labelled);
    xfree(raw);

    if (seq == 0) { break; }

    // and store the sequence
    dat->seq[dat->nseq++] = seq;
    dat->mlen = max(dat->mlen, seq->len);

  }

  // if no sequence was read, free memory
  if (dat->nseq == 0) {
    xfree(dat->seq);
    xfree(dat);

    return 0;
  }

  return dat;
}

static dat_t *ld_dat(rdr_t *reader, VALUE data, bool labelled) {
  FILE *file;
  dat_t *dat = (dat_t*)0;

  switch (TYPE(data)) {
    case T_STRING:
      file = ufopen(data, "r");
      dat = rdr_readdat(reader, file, labelled);
      fclose(file);
      break;

    case T_ARRAY:
      dat = to_dat(reader, data, labelled);
      break;

    default:
      fatal("invalid data type (expected instance of String or Array)");
  }

  return dat;
}


static VALUE model_train(VALUE self, VALUE train, VALUE devel) {
  FILE *file;
  mdl_t *model = get_model(self);
  trn_t trn = trn_get(model->opt->algo);
  model->type = typ_get(model->opt->type);

  // Load the pattern file. This will unlock the database if previously
  // locked by loading a model.
  if (model->opt->pattern) {
    info("load patterns");
    file = fopen(model->opt->pattern, "r");

    if (!file) {
      pfatal("failed to train model: failed to load pattern file '%s'",
        model->opt->pattern);
    }

    rdr_loadpat(model->reader, file);
    fclose(file);

    qrk_lock(model->reader->obs, false);
  }


  // Load the training data. When this is done we lock the quarks as we
  // don't want to put in the model, informations present only in the
  // development set.
  model->train = ld_dat(model->reader, train, true);

  qrk_lock(model->reader->lbl, true);
  qrk_lock(model->reader->obs, true);

  if (!model->train || model->train->nseq == 0) {
    fatal("failed to train model: no training data loaded");
  }

  // If present, load the development set in the model. If not specified,
  // the training dataset will be used instead.
  if (TYPE(devel) != T_NIL) {
    model->devel = ld_dat(model->reader, devel, true);
  }

	// Initialize the model. If a previous model was loaded, this will be
	// just a resync, else the model structure will be created.
  info((model->theta == NULL) ? "initialize model" : "re-sync model");
	mdl_sync(model);

	info("nb train:    %"PRIu32"", model->train->nseq);
	if (model->devel != NULL)
		info("nb devel:    %"PRIu32"", model->devel->nseq);
	info("nb labels:   %"PRIu32"", model->nlbl);
	info("nb blocks:   %"PRIu64"", model->nobs);
	info("nb features: %"PRIu64"", model->nftr);

	info("training model with %s", model->opt->algo);
  uit_setup(model);
  trn(model);
  uit_cleanup(model);

  if (model->opt->compact) {
		const uint64_t O = model->nobs;
		const uint64_t F = model->nftr;
		info("compacting model");
		mdl_compact(model);
		info("%8"PRIu64" observations removed", O - model->nobs);
		info("%8"PRIu64" features removed", F - model->nftr);
  }

  return self;
}

// Returns a sorted list of all labels in the Model's label database.
static VALUE model_labels(VALUE self) {
  mdl_t *model = get_model(self);
  const uint32_t Y = model->nlbl;

  qrk_t *lp = model->reader->lbl;

  VALUE labels = rb_ary_new2(Y);
  for (unsigned int i = 0; i < Y; ++i) {
    rb_ary_push(labels, rb_str_new2(qrk_id2str(lp, i)));
  }

  rb_funcall(labels, rb_intern("sort!"), 0);

  return labels;
}

static VALUE decode_sequence(VALUE self, mdl_t *model, raw_t *raw) {
  qrk_t *lbls = model->reader->lbl;

  const unsigned int Y = model->nlbl;
  const unsigned int N = model->opt->nbest;

  seq_t *seq = rdr_raw2seq(model->reader, raw, model->opt->check);

  const unsigned int T = seq->len;
  unsigned int n, t, tcnt = 0, terr = 0, scnt = 0, serr = 0, stat[3][Y];

  uint32_t *out = wapiti_xmalloc(sizeof(uint32_t) * T * N);
  double *psc = wapiti_xmalloc(sizeof(double) * T * N);
  double *scs = wapiti_xmalloc(sizeof(double) * N);

  VALUE sequence, tokens;

  if (N == 1) {
    tag_viterbi(model, seq, out, scs, psc);
  } else {
    tag_nbviterbi(model, seq, N, (void*)out, scs, (void*)psc);
  }

  sequence = rb_ary_new();

  for (t = 0; t < T; ++t) {
    tokens = rb_ary_new();

    if (!model->opt->label) {
      VALUE token = rb_str_new2(raw->lines[t]);

      int enc = rb_enc_find_index("UTF-8");
      rb_enc_associate_index(token, enc);

      rb_ary_push(tokens, token);
    }

    for (n = 0; n < N; ++n) {
      uint64_t lbl = out[t * N + n];
      rb_ary_push(tokens, rb_str_new2(qrk_id2str(lbls, lbl)));

      // output individual score
      if (model->opt->outsc) {
        rb_ary_push(tokens, rb_float_new(psc[t * N + n]));
      }
    }

    // yield token/label pair to block if given
    if (rb_block_given_p()) {
      tokens = rb_yield(tokens);
    }

    rb_ary_push(sequence, tokens);

    // TODO output sequence score: scs[n] (float)
  }

  // Statistics

  if (model->opt->check) {
    int err = 0;
    uint32_t lbl = 0;

    for (t = 0; t < T; ++t) {
      lbl = seq->pos[t].lbl;

      // ((uint32_t)-1) is a magic value for no asigned token
      if (lbl != ((uint32_t)-1)) {
        stat[0][lbl]++;
      }
      stat[1][out[t * N]]++;

      if (lbl != out[t * N]) {
        terr++;
        err = 1;
      } else {
        stat[2][out[t * N]]++;
      }
    }

    tcnt = FIX2INT(rb_ivar_get(self, rb_intern("@token_count")));
    rb_ivar_set(self, rb_intern("@token_count"), INT2FIX(tcnt + (unsigned int)T));

    terr += FIX2INT(rb_ivar_get(self, rb_intern("@token_errors")));
    rb_ivar_set(self, rb_intern("@token_errors"), INT2FIX(terr));

    scnt = FIX2INT(rb_ivar_get(self, rb_intern("@sequence_count")));
    rb_ivar_set(self, rb_intern("@sequence_count"), INT2FIX(++scnt));

    serr = FIX2INT(rb_ivar_get(self, rb_intern("@sequence_errors")));
    rb_ivar_set(self, rb_intern("@sequence_errors"), INT2FIX(serr + err));
  }

  // Cleanup memory used for this sequence
  xfree(scs);
  xfree(psc);
  xfree(out);

  rdr_freeseq(seq);

  return sequence;
}

static VALUE decode_sequence_array(VALUE self, VALUE array) {
  Check_Type(array, T_ARRAY);
  const unsigned int n = RARRAY_LEN(array);

  mdl_t *model = get_model(self);
  raw_t *raw;

  const unsigned int N = model->opt->nbest;
  unsigned int i, j;

  VALUE result = rb_ary_new2(n * N), sequence;

  for (i = 0; i < n; ++i) {
    sequence = rb_ary_entry(array, i);
    Check_Type(sequence, T_ARRAY);

    const unsigned int k = RARRAY_LEN(sequence);
    raw = wapiti_xmalloc(sizeof(raw_t) + sizeof(char*) * k);
    raw->len = k;

    for (j = 0; j < k; ++j) {
      VALUE line = rb_ary_entry(sequence, j);
      Check_Type(line, T_STRING);
      raw->lines[j] = StringValueCStr(line);
    }

    rb_ary_push(result, decode_sequence(self, model, raw));

    xfree(raw);
  }

  return result;
}

static VALUE decode_sequence_file(VALUE self, VALUE path) {
  FILE *file = ufopen(path, "r");
  mdl_t *model = get_model(self);
  raw_t *raw;

  VALUE result = rb_ary_new();

  // Next read the input file sequence by sequence and label them, we have
  // to take care of not discarding the raw input as we want to send it
  // back to the output with the additional predicted labels.
  while (!feof(file)) {
    // So, first read an input sequence keeping the raw_t object
    // available, and label it with Viterbi.
    if ((raw = rdr_readraw(model->reader, file)) == 0) {
      break;
    }

    rb_ary_push(result, decode_sequence(self, model, raw));
    rdr_freeraw(raw);
  }

  return result;
}

// call-seq:
//   m.label(tokens, options = {})  # => array of labelled tokens
//   m.label(filename, options = {}) # => array of labelled tokens
//
static VALUE model_label(VALUE self, VALUE data) {
  VALUE result = (VALUE)0;

  switch (TYPE(data)) {
    case T_STRING:
      result = decode_sequence_file(self, data);
      break;
    case T_ARRAY:
      result = decode_sequence_array(self, data);
      break;
    default:
      fatal("failed to label data: invalid data (expected type String or Array)");
  }

  return result;
}

static void Init_model() {
  cModel = rb_define_class_under(mWapiti, "Model", rb_cObject);
  rb_define_alloc_func(cModel, allocate_model);
  rb_define_attr(cModel, "options", 1, 0);

  rb_define_method(cModel, "initialize", initialize_model, -1);
  rb_define_method(cModel, "nlbl", model_nlbl, 0);
  rb_define_method(cModel, "labels", model_labels, 0);
  rb_define_method(cModel, "nobs", model_nobs, 0);
  rb_define_alias(cModel, "observations", "nobs");
  rb_define_method(cModel, "nftr", model_nftr, 0);
  rb_define_alias(cModel, "features", "nftr");
  rb_define_method(cModel, "sync", model_sync, 0);
  rb_define_method(cModel, "compact", model_compact, 0);
  rb_define_method(cModel, "save", model_save, -1);
  rb_define_method(cModel, "load", model_load, -1);
  rb_define_method(cModel, "train", model_train, 2);
  rb_define_method(cModel, "label", model_label, 1);
}

/* --- Wapiti Extension Entry Point --- */

void Init_native() {
  mWapiti = rb_const_get(rb_mKernel, rb_intern("Wapiti"));
  mNative = rb_define_module_under(mWapiti, "Native");

  cArgumentError = rb_const_get(rb_mKernel, rb_intern("ArgumentError"));
  cNativeError = rb_const_get(mWapiti, rb_intern("NativeError"));
  cLogger = rb_funcall(mWapiti, rb_intern("log"), 0);

  rb_define_const(mNative, "VERSION", rb_str_new2(VERSION));

  Init_options();
  Init_model();
}

69d00f79e0d5941ec4c30c6c0fea9f009e2b1d9f

[rejour-rehype/citations] make checks for the kind of citations used.

[rejour-rehype/citations] make checks for the kind of citations used.

j(node, 'element', { name: 'article' }, all(j, article)),

])

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/handlers/xref.ts#L43

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Article, Parent, TagHavers, Xref } from 'ooxast'
import { Command } from 'jast'
import { all } from '../all'
import { J, Node, Root } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'fn',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to jast: citation.

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              value: node.children
                .map((node) => {
                  //@ts-ignore
                  const n = node.value.replace(/[\[\], ]/g, '')
                  return n ? `bib${n}` : undefined
                })
                .filter((n) => !!n)
                .join(','),
            },
          ],
        },
      ])
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error
              value: 'bib' + node.children[0]?.value?.replace(/[\[\]]/g, ''),
            },
          ],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

7467d8c4b8ec67f234cd993ed3d889d2884752ad

[csl-to-jast] write a function that converts CSL to JAST frontmatter

[csl-to-jast] write a function that converts CSL to JAST frontmatter

https://github.com/JournalOfTrialAndError/JOTE/blob/92b9c45361dbf38d7f9be244270ef81e40facd8c/libs/rejour/csl-to-jast/src/lib/csl-to-jast.ts#L28

  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }

2faeaeb983daadb9a066ceadecb0ea753527c0ee

Update to latest Jest snapshotFormat

Update to latest Jest snapshotFormat

By default Nx has kept the older style of Jest Snapshot formats

to prevent breaking of any existing tests with snapshots.

It's recommend you update to the latest format.

You can do this by removing snapshotFormat property

and running tests with --update-snapshot flag.

Example: "nx affected --targets=test --update-snapshot"

More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format

/* TODO: Update to latest Jest snapshotFormat

  moduleNameMapper: pathsToModuleNameMapper(paths, {
    prefix: `${__dirname}/`,
  }),
  /* TODO: Update to latest Jest snapshotFormat
   * By default Nx has kept the older style of Jest Snapshot formats
   * to prevent breaking of any existing tests with snapshots.
   * It's recommend you update to the latest format.
   * You can do this by removing snapshotFormat property
   * and running tests with --update-snapshot flag.
   * Example: "nx affected --targets=test --update-snapshot"
   * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
   */
  snapshotFormat: { escapeString: true, printBasicPrototype: true },
}

cb6c2d74e5f06489161b709ea7f755cc60c8b844

[csl-to-jts] add reviewer support

[csl-to-jts] add reviewer support

https://github.com/JournalOfTrialAndError/JOTE/blob/4b14de45df7a9618fe579a3052560971317727ef/libs/rejour/csl-to-jast/src/lib/csl-to-jast.ts#L167

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  ElementCitation,
  Publisher,
  PubId,
  PersonGroup,
  Source,
  Date,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jjast'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

// @ts-ignore
export function cslToFront(data: CSL): Front {}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-ignore no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-ignore
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? //@ts-ignore
      x('personGroup', { personGroupType: 'author' }, names)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

90cafdc69651bff1d27cab5e30556eb7677f911e

[rejour-relatex]: make footnote identification less arbitrary, like a counter or...

[rejour-relatex]: make footnote identification less arbitrary, like a counter or something

@ts-ignore

https://github.com/JournalOfTrialAndError/JOTE/blob/4b14de45df7a9618fe579a3052560971317727ef/libs/rejour/jast-util-to-texast/src/lib/handlers/xref.ts#L90

  // Maybe add a new type to texast: citation.

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  console.log(node.attributes)
  switch (node.attributes.refType) {
    case 'bibr': {
      return j(
        node,
        'command',
        { name: j.citationAnalyzer(node) || 'autocite' },
        [
          {
            type: 'commandArg',
            children: [
              {
                type: 'text',
                value:
                  node.attributes.rid ||
                  node.children
                    .map((node) => {
                      //@ts-ignore
                      const n = node.value.replace(/[\[\], ]/g, '')
                      return n ? `bib${n}` : undefined
                    })
                    .filter((n) => !!n)
                    .join(','),
              },
            ],
          },
        ]
      )
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error
              value: 'bib' + node.children[0]?.value?.replace(/[\[\]]/g, ''),
            },
          ],
        },
      ])
    }
    case 'fn': {
      const fnContent = j.footnotes[
        // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
        //@ts-ignore
        parseInt(node.children?.[0]?.value?.replace(/[\[\]]/g, '')) - 1
      ] as any
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          children:
            fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }

ac31ceffe8d80dcdddfc216547383e42440ab5fa

[rejour-rehype/citations] make checks for the kind of citations used.

[rejour-rehype/citations] make checks for the kind of citations used.

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-to-texast/src/lib/handlers/xref.ts#L47

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Xref, Text } from 'jast-types'
import { CommandArg } from 'texast'
import { J } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'footnote',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  const labelToText: { [key: string]: string } = {
    page: 'pp.',
    appendix: 'App.',
  }

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      const customType = node.attributes.customType

      // TODO: [rejour-relatex] make latex cite command setting more modular and customizable
      let command
      let pre
      let post
      if (customType) {
        const customData = JSON.parse(customType)
        const { prefix, infix, label, locator, mode, suffix } = customData

        const pref = (mode ? infix : prefix) || ''

        const suff = `${
          label && label !== 'none'
            ? `${labelToText[label] || label || 'pp.'} `
            : ''
        }${locator || ''}`
        command = mode ? 'textcite' : 'parencite'

        if (pref) pre = pref
        if (suff) post = suff
      }

      const optCommandArgs = createOptCiteArgs(pre, post)
      return j(
        node,
        'command',
        { name: command || j.citationAnalyzer(node) || 'autocite' },
        [
          ...optCommandArgs,
          {
            type: 'commandArg',
            children: [
              {
                type: 'text',
                value:
                  node.attributes.rid ||
                  node.children
                    .map((node) => {
                      //@ts-expect-error it is text, it has value
                      const n = node.value.replace(/[[\], ]/g, '')
                      return n ? `bib${n}` : undefined
                    })
                    .filter((n) => !!n)
                    .join(','),
              },
            ],
          },
        ]
      )
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error It is text, it has value
              value: 'bib' + node.children[0]?.value?.replace(/[[\]]/g, ''),
            },
          ],
        },
      ])
    }
    case 'fn': {
      const fnContent = j.footnotes[
        // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
        // @ts-expect-error it is text, it has value
        parseInt(node.children?.[0]?.value?.replace(/[[\]]/g, '')) - 1
      ] as any
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          children:
            fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

function createOptCiteArgs(pre?: string, post?: string) {
  if (!pre && !post) return []
  if (!post) {
    return [
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: '',
          } as Text,
        ],
      } as CommandArg,
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: pre,
          } as Text,
        ],
      } as CommandArg,
    ]
  }

  return [
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: post,
        } as Text,
      ],
    } as CommandArg,
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: pre,
        } as Text,
      ],
    } as CommandArg,
  ]
}

a0610b88b919e893268b0bb1cad565c1c51ac835

[ooxast-util-to-jast] Store information about column alignment in tabular

[ooxast-util-to-jast] Store information about column alignment in tabular

error out while underfull ones are fine

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/handlers/table.ts#L16

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Table, isElement, Element, Tr, Col } from 'ooxast'
import { all } from '../all'
import { J, Node } from '../types'
import { visit as origVisit } from 'unist-util-visit'
import { CommandArg } from 'jast'

// try to turn of typechecking for visit as it it bugged
// https://github.com/syntax-tree/unist-util-visit/issues/33
const visit = origVisit as any
export function table(j: J, table: Table) {
  let columns: string[] = []
  let hasCols = false
  // tables can be def'd in terms of cols or rows
  // TODO: [ooxast-util-to-jast] Store information about column alignment in tabular
  visit(
    table,
    (node: Node) => isElement(node) && ['col', 'tr'].includes(node.name),
    (node: Col | Tr) => {
      if (node.name === 'col') {
        hasCols = true
        columns.push('c')
        return
      }

      if (hasCols) return
      let tempCols: string[] = []

      node?.children?.forEach((child) => {
        isElement(child) && child.name === 'td' && tempCols.push('c')
      })
      // Just make the table as wide as it needs to be, overfull tables
      // error out while  underfull ones are fine
      if (tempCols.length > columns.length) columns = tempCols
      tempCols = []
      return
    }
  )

  const colAlignment = columns.join(` ${j.columnSeparator ? '|' : ''} `)
  const colAlignArg: CommandArg = {
    type: 'commandArg',
    children: [{ type: 'text', value: colAlignment }],
  }

  const contents = all(j, table)
  contents.unshift(colAlignArg)

  return { type: 'environment', name: 'tabular', children: contents }
}

ee96e422f7f1f07b82eafc9fda2d9a044c20d64f

[rehype-notion] Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in

I try to get rehype-notion to work, but always get the error:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /Users/zirkelc/Code/html-to-notion/node_modules/rehype-notion/package.json imported from /Users/zirkelc/Code/html-to-notion/rehype-notion.js
    at new NodeError (node:internal/errors:399:5)
    at exportsNotFound (node:internal/modules/esm/resolve:361:10)
    at packageExportsResolve (node:internal/modules/esm/resolve:641:13)
    at packageResolve (node:internal/modules/esm/resolve:872:14)
    at moduleResolve (node:internal/modules/esm/resolve:938:20)
    at defaultResolve (node:internal/modules/esm/resolve:1153:11)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:838:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:77:40) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

That's the JS code:

import { unified } from 'unified'
import { parse } from 'rehype-parse'
import { rehypeNotion } from 'rehype-notion'

const html = `
  <h1>Heading</h1>
  <p>Paragraph</p>
`


const vfile = await unified()
	.use(parse)
	.use(rehypeNotion)
	.process(html);

const notionBlocks = vfile.result
console.log(notionBlocks)

The package.json of rehype-notion seems to miss an export definition for import since its a ESM package. However, adding that export didn't solve the issue:

  "exports": {
    ".": {
      "import": "./src/index.js",
      "require": "./src/index.js"
    }
  },

It then fails with this error:

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/zirkelc/Code/html-to-notion/node_modules/.pnpm/[email protected]_hjujqg5lc3gpvhzaa4sfejxtk4/node_modules/rehype-notion/src/lib/rehype-notion' imported from /Users/zirkelc/Code/html-to-notion/node_modules/.pnpm/[email protected]_hjujqg5lc3gpvhzaa4sfejxtk4/node_modules/rehype-notion/src/index.js
    at new NodeError (node:internal/errors:399:5)
    at finalizeResolution (node:internal/modules/esm/resolve:326:11)
    at moduleResolve (node:internal/modules/esm/resolve:945:10)
    at defaultResolve (node:internal/modules/esm/resolve:1153:11)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:838:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:77:40)
    at link (node:internal/modules/esm/module_job:76:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Any idea what might be wrong here?

[jast-util-to-texast] Check whether col how multirow elements

[jast-util-to-texast] Check whether col how multirow elements

error out while underfull ones are fine

// TODO: [jast-util-to-texast] Check whether col how multirow elements

    (node: Col | Tr) => {
      if (node.name === 'col') {
        hasCols = true
        columns.push('l')

        // TODO: [jast-util-to-texast] Check whether col how multirow elements
        numberOfRows = node.children.length

        return
      }

      if (hasCols) return

      numberOfRows += 1

      let tempCols: string[] = []

      node?.children?.forEach((child) => {
        if (!isElement(child)) return

        if (child.name !== 'td') return

        for (let i = 0; i < (child?.attributes?.colspan ?? 1); i++) {
          tempCols.push('l')
        }
      })
      // Just make the table as wide as it needs to be, overfull tables
      // error out while  underfull ones are fine

b7572ce66285efd133d078766b47d03395ff98bb

[rejour-relatex]: make footnote identification less arbitrary, like a counter or...

[rejour-relatex]: make footnote identification less arbitrary, like a counter or something

@ts-expect-error it is text, it has value

j(node, 'element', { name: 'article' }, all(j, article)),

])

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-to-texast/src/lib/handlers/xref.ts#L117

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Xref, Text } from 'jast-types'
import { CommandArg } from 'texast'
import { J } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'footnote',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  const labelToText: { [key: string]: string } = {
    page: 'pp.',
    appendix: 'App.',
  }

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      const customType = node.attributes.customType

      // TODO: [rejour-relatex] make latex cite command setting more modular and customizable
      let command
      let pre
      let post
      if (customType) {
        const customData = JSON.parse(customType)
        const { prefix, infix, label, locator, mode, suffix } = customData

        const pref = (mode ? infix : prefix) || ''

        const suff = `${
          label && label !== 'none'
            ? `${labelToText[label] || label || 'pp.'} `
            : ''
        }${locator || ''}`
        command = mode ? 'textcite' : 'parencite'

        if (pref) pre = pref
        if (suff) post = suff
      }

      const optCommandArgs = createOptCiteArgs(pre, post)
      return j(
        node,
        'command',
        { name: command || j.citationAnalyzer(node) || 'autocite' },
        [
          ...optCommandArgs,
          {
            type: 'commandArg',
            children: [
              {
                type: 'text',
                value:
                  node.attributes.rid ||
                  node.children
                    .map((node) => {
                      //@ts-expect-error it is text, it has value
                      const n = node.value.replace(/[[\], ]/g, '')
                      return n ? `bib${n}` : undefined
                    })
                    .filter((n) => !!n)
                    .join(','),
              },
            ],
          },
        ]
      )
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error It is text, it has value
              value: 'bib' + node.children[0]?.value?.replace(/[[\]]/g, ''),
            },
          ],
        },
      ])
    }
    case 'fn': {
      const fnContent = j.footnotes[
        // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
        // @ts-expect-error it is text, it has value
        parseInt(node.children?.[0]?.value?.replace(/[[\]]/g, '')) - 1
      ] as any
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          children:
            fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

function createOptCiteArgs(pre?: string, post?: string) {
  if (!pre && !post) return []
  if (!post) {
    return [
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: '',
          } as Text,
        ],
      } as CommandArg,
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: pre,
          } as Text,
        ],
      } as CommandArg,
    ]
  }

  return [
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: post,
        } as Text,
      ],
    } as CommandArg,
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: pre,
        } as Text,
      ],
    } as CommandArg,
  ]
}

9ba8de089c6ddac859a96b08d3db0f3c01f5ac65

[rejour-rehype/citations] make citation parsing less hardcoded

[rejour-rehype/citations] make citation parsing less hardcoded

Maybe add a new type to texast: citation.

https://github.com/JournalOfTrialAndError/JOTE/blob/fa79db2d327a6102b0bf28ba86a9848f05673d05/libs/rejour/jast-util-to-texast/src/lib/handlers/xref.ts#L40

    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'fn',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.properties.refType) {
    case 'bibr': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              value: node.children
                .map((node) => {
                  //@ts-ignore
                  const n = node.value.replace(/[\[\], ]/g, '')
                  return n ? `bib${n}` : undefined
                })
                .filter((n) => !!n)
                .join(','),
            },
          ],
        },
      ])
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error
              value: 'bib' + node.children[0]?.value?.replace(/[\[\]]/g, ''),
            },
          ],
        },

1807fa0fbb8833717cb0806011f25107c765d856

[rejour-rehype/citations] make citation parsing less hardcoded

[rejour-rehype/citations] make citation parsing less hardcoded

Maybe add a new type to jast: citation.

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/handlers/xref.ts#L40

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Article, Parent, TagHavers, Xref } from 'ooxast'
import { Command } from 'jast'
import { all } from '../all'
import { J, Node, Root } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'fn',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to jast: citation.

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              value: node.children
                .map((node) => {
                  //@ts-ignore
                  const n = node.value.replace(/[\[\], ]/g, '')
                  return n ? `bib${n}` : undefined
                })
                .filter((n) => !!n)
                .join(','),
            },
          ],
        },
      ])
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error
              value: 'bib' + node.children[0]?.value?.replace(/[\[\]]/g, ''),
            },
          ],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

9d5fc25a5fc81148727cedb458cfc78dcc15fe80

[rejour-rehype/citations] make citation parsing less hardcoded

[rejour-rehype/citations] make citation parsing less hardcoded

Maybe add a new type to texast: citation.

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-to-texast/src/lib/handlers/xref.ts#L39

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Xref, Text } from 'jast-types'
import { CommandArg } from 'texast'
import { J } from '../types'
import { wrapCommandArg } from '../util/wrap-command-arg'

export function xref(j: J, node: Xref) {
  //  if (!article) {
  const refTypeMap = {
    bibr: 'cite',
    aff: 'ref',
    app: 'ref',
    bio: 'bio',
    default: 'ref',
    'author-notes': 'author-notes',
    award: 'award',
    'boxed-text': 'boxed-text',
    chem: 'chem',
    collab: 'collab',
    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'footnote',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  const labelToText: { [key: string]: string } = {
    page: 'pp.',
    appendix: 'App.',
  }

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      const customType = node.attributes.customType

      // TODO: [rejour-relatex] make latex cite command setting more modular and customizable
      let command
      let pre
      let post
      if (customType) {
        const customData = JSON.parse(customType)
        const { prefix, infix, label, locator, mode, suffix } = customData

        const pref = (mode ? infix : prefix) || ''

        const suff = `${
          label && label !== 'none'
            ? `${labelToText[label] || label || 'pp.'} `
            : ''
        }${locator || ''}`
        command = mode ? 'textcite' : 'parencite'

        if (pref) pre = pref
        if (suff) post = suff
      }

      const optCommandArgs = createOptCiteArgs(pre, post)
      return j(
        node,
        'command',
        { name: command || j.citationAnalyzer(node) || 'autocite' },
        [
          ...optCommandArgs,
          {
            type: 'commandArg',
            children: [
              {
                type: 'text',
                value:
                  node.attributes.rid ||
                  node.children
                    .map((node) => {
                      //@ts-expect-error it is text, it has value
                      const n = node.value.replace(/[[\], ]/g, '')
                      return n ? `bib${n}` : undefined
                    })
                    .filter((n) => !!n)
                    .join(','),
              },
            ],
          },
        ]
      )
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error It is text, it has value
              value: 'bib' + node.children[0]?.value?.replace(/[[\]]/g, ''),
            },
          ],
        },
      ])
    }
    case 'fn': {
      const fnContent = j.footnotes[
        // TODO: [rejour-relatex]: make footnote identification less arbitrary, like a counter or something
        // @ts-expect-error it is text, it has value
        parseInt(node.children?.[0]?.value?.replace(/[[\]]/g, '')) - 1
      ] as any
      return j(node, 'command', { name: 'footnote' }, [
        {
          type: 'commandArg',
          children:
            fnContent.type === 'paragraph' ? fnContent.children : [fnContent],
        },
      ])
    }
    default:
      return j(
        node,
        'command',
        { name: refTypeMap[node.attributes.refType || 'default'] || 'ref' },
        [wrapCommandArg(j, node.children)]
      )
  }
  //  }

  // return j(article, 'root', [
  //   j(node, 'element', { name: 'article' }, all(j, article)),
  // ])
}

function createOptCiteArgs(pre?: string, post?: string) {
  if (!pre && !post) return []
  if (!post) {
    return [
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: '',
          } as Text,
        ],
      } as CommandArg,
      {
        type: 'commandArg',
        optional: true,
        children: [
          {
            type: 'text',
            value: pre,
          } as Text,
        ],
      } as CommandArg,
    ]
  }

  return [
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: post,
        } as Text,
      ],
    } as CommandArg,
    {
      type: 'commandArg',
      optional: true,
      children: [
        {
          type: 'text',
          value: pre,
        } as Text,
      ],
    } as CommandArg,
  ]
}

63cc5809a0d1f1f2af4b3e9d3a5da9e856c2af7a

[csl-to-jts] do something with abstract maybe

[csl-to-jts] do something with abstract maybe

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-from-csl/src/lib/csl-to-jast.ts#L166

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  PubId,
  PersonGroup,
  Source,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jast-types'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-expect-error no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-expect-error I can assign it to this dw bby
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? (x('personGroup', { personGroupType: 'author' }, names) as PersonGroup)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

46389a98c894756e6b18660b686da377e5482cc8

[rejour-relatex] Cast JastRoot to TexastRoot better

[rejour-relatex] Cast JastRoot to TexastRoot better

@ts-ignore there should be a better way to cast this

https://github.com/JournalOfTrialAndError/JOTE/blob/b3f55b145db6127b88ab8c195e3ca988dc4ee5bb/libs/reoff/reoff-rejour/src/lib/reoff-reoff-rejour.ts#L39

import { toTexast, Options } from 'jast-util-to-texast'
import { Root as JastRoot } from 'jjast'
import { Root as TexastRoot } from 'texast'
import {
  Plugin,
  Processor as UnifiedProcessor,
  TransformCallback,
  Transformer,
} from 'unified'
import { VFile } from 'vfile'
type Processor = UnifiedProcessor<any, any, any, any>

/**
 * Bridge-mode.
 * Runs the destination with the new mdast tree.
 *
 */
function bridge(
  destination: Processor,
  options?: Options
): void | Transformer<JastRoot, JastRoot> {
  return (node, file, next) => {
    //@ts-ignore there should be a better way to cast this
    destination.run(toTexast(node, options), file, (error) => {
      next(error)
    })
  }
}

/**
 * Mutate-mode.
 * Further transformers run on the texast tree.
 */
function mutate(
  options: void | Options | undefined = {}
): ReturnType<Plugin<[Options?] | void[], JastRoot, TexastRoot>> {
  //Transformer<JastRoot, JastRoot> | void {
  return (node) => {
    // TODO: [rejour-relatex] Cast JastRoot to TexastRoot better
    //@ts-ignore there should be a better way to cast this
    const result = toTexast(node, options) as TexastRoot
    return result
  }
}

/**
 * Plugin to bridge or mutate to rehype.
 *
 * If a destination is given, runs the destination with the new mdast
 * tree (bridge-mode).
 * Without destination, returns the mdast tree: further plugins run on that
 * tree (mutate-mode).
 *
 * @param destination
 *   Optional unified processor.
 * @param options
 *   Options passed to `jast-util-to-texast`.
 */
const rejourRelatex = function (
  destination?: Processor | Options,
  options?: Options
) {
  let settings: Options | undefined
  let processor: Processor | undefined

  if (typeof destination === 'function') {
    processor = destination
    settings = options
  } else {
    settings = destination
  }

  if (settings?.document === undefined || settings.document === null) {
    settings = Object.assign({}, settings, { document: true })
  }

  return processor ? bridge(processor, settings) : mutate(settings)
} as Plugin<[Processor, Options?], JastRoot> &
  Plugin<[Options?] | void[], JastRoot, TexastRoot>

export default rejourRelatex

3aadda35ede377c7bb173f0a2685a3c928807fc7

missing assertions

missing assertions

bib = BibTeX.open(Test.fixtures(:errors), :debug => false)

#refute_nil(bib)

end

bib = BibTeX.parse(file, :debug => false)

assert_equal file.gsub(/[\s]+/, ''), bib.to_s.gsub(/[\s]+/, '')

bib = BibTeX::Bibliography.new

bib << BibTeX::Entry.new({

:bibtex_type => :book,

:key => 'rails',

:address => 'Raleigh, North Carolina',

:author => 'Ruby, Sam and Thomas, Dave and Hansson Heinemeier, David',

:booktitle => 'Agile Web Development with Rails',

:edition => 'third',

:keywords => 'ruby, rails',

:publisher => 'The Pragmatic Bookshelf',

:series => 'The Facets of Ruby',

:title => 'Agile Web Development with Rails',

:year => '2009'

})

assert_equal(file.gsub(/[\s]+/, ''), bib.to_s.gsub(/[\s]+/, ''))

require 'helper.rb'
require 'timeout'

module BibTeX
  class TestBibtex < Minitest::Unit::TestCase
    def setup; end

    def teardown; end

    def test_empty
      bib = BibTeX::Bibliography.open(Test.fixtures(:empty), debug: false)
      refute_nil(bib)
      assert_equal(BibTeX::Bibliography, bib.class)
      assert(bib.empty?)
    end

    def test_no_bibtex
      bib = BibTeX::Bibliography.open(Test.fixtures(:no_bibtex), debug: false)
      refute_nil(bib)
      assert_equal(BibTeX::Bibliography, bib.class)
      assert(bib.empty?)
    end

    def test_decoret
      bib = BibTeX::Bibliography.open(Test.fixtures(:decoret), debug: false)
      assert_equal(15, bib.length)
      assert_equal([BibTeX::Entry, BibTeX::Comment, BibTeX::String, BibTeX::Preamble], bib.data.map(&:class).uniq)
      assert_equal('py03', bib.data[0].key)
      assert_equal(:article, bib[:py03].type)
      assert_equal("D\\'ecoret, Xavier", bib[:py03][:author].to_s)
      assert_equal('PyBiTex', bib[:py03][:title])
      assert_equal('2003', bib[:py03][:year])
      assert_equal(:article, bib[:key03].type)
      assert_equal('A {bunch {of} braces {in}} title', bib[:key03][:title])
      # TODO: missing assertions
    end

    # def test_errors
    #   bib = BibTeX.open(Test.fixtures(:errors), :debug => false)
    #   #refute_nil(bib)
    # end

    def test_bibdesk
      bib = BibTeX::Bibliography.open(Test.fixtures(:bibdesk), debug: false)
      assert_equal 3, bib.length
      assert_equal 'rails', bib[0].key
      assert_equal '2010-08-05 10:06:32 +0200', bib[:dragon]['date-modified']
    end

    def test_roundtrip
      # file = File.read(Test.fixtures(:roundtrip))
      # bib = BibTeX.parse(file, :debug => false)
      # assert_equal file.gsub(/[\s]+/, ''), bib.to_s.gsub(/[\s]+/, '')
    end

    def test_construct
      # file = File.read(Test.fixtures(:roundtrip))
      # bib = BibTeX::Bibliography.new
      # bib << BibTeX::Entry.new({
      #   :bibtex_type => :book,
      #   :key => 'rails',
      #   :address => 'Raleigh, North Carolina',
      #   :author => 'Ruby, Sam and Thomas, Dave and Hansson Heinemeier, David',
      #   :booktitle => 'Agile Web Development with Rails',
      #   :edition => 'third',
      #   :keywords => 'ruby, rails',
      #   :publisher => 'The Pragmatic Bookshelf',
      #   :series => 'The Facets of Ruby',
      #   :title => 'Agile Web Development with Rails',
      #   :year => '2009'
      # })
      # assert_equal(file.gsub(/[\s]+/, ''), bib.to_s.gsub(/[\s]+/, ''))
    end

    def test_parse
      bib = BibTeX::Bibliography.new
      bib.add(BibTeX::Element.parse(' @string{ pragprog = "The Pragmatic Bookshelf" } '))
      bib.add(BibTeX::Element.parse(<<-END))
      @book{rails,
        address = {Raleigh, North Carolina},
        author = {Ruby, Sam and Thomas, Dave and Hansson Heinemeier, David},
        booktitle = {Agile Web Development with Rails},
        edition = {third},
        keywords = {ruby, rails},
        publisher = pragprog,
        series = {The Facets of Ruby},
        title = {Agile Web Development with Rails},
        year = {2009}
      }
      END

      assert_equal(2, bib.length)
      refute_nil(bib[:rails])
      bib.replace_strings
      assert_equal 'The Pragmatic Bookshelf', bib['rails'].publisher
    end

    def test_logger_can_be_assigned
      logger = BibTeX.log
      BibTeX.log = logger
    end

    def test_missing_key
      assert_raises(BibTeX::ParseError) do
        BibTeX.parse(<<EOF)
        @article{}
EOF
      end
      assert(
        BibTeX.parse(<<EOF, allow_missing_keys: true)
        @article{}
EOF
      )
      Timeout.timeout(2) do
        BibTeX.parse(<<EOF, allow_missing_keys: true)
        @article{},
        @article{}
EOF
      end
    end
  end
end

e013f1558fce8a0ab5f508ba5ce09374b826ff3c

[csl-to-jts] add reviewer support

[csl-to-jts] add reviewer support

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-from-csl/src/lib/csl-to-jast.ts#L165

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  PubId,
  PersonGroup,
  Source,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jast-types'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-expect-error no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-expect-error I can assign it to this dw bby
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? (x('personGroup', { personGroupType: 'author' }, names) as PersonGroup)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

8e752638212cfe0b8d8cd9b26eca2939d80b7849

[csl-to-jts] use citation key if available

[csl-to-jts] use citation key if available

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-from-csl/src/lib/csl-to-jast.ts#L168

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  PubId,
  PersonGroup,
  Source,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jast-types'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-expect-error no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-expect-error I can assign it to this dw bby
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? (x('personGroup', { personGroupType: 'author' }, names) as PersonGroup)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

977e4dc11a09fae311c5a4d9b56f4745341419ba

[rejour-rehype/citations] make checks for the kind of citations used.

[rejour-rehype/citations] make checks for the kind of citations used.

https://github.com/JournalOfTrialAndError/JOTE/blob/fa79db2d327a6102b0bf28ba86a9848f05673d05/libs/rejour/jast-util-to-texast/src/lib/handlers/xref.ts#L43

    contrib: 'contrib',
    corresp: 'corresp',
    custom: 'custom',
    'disp-formula': 'eqref',
    fig: 'ref',
    fn: 'fn',
    kwd: 'kwd',
    list: 'list',
    other: 'other',
    plate: 'plate',
    scheme: 'scheme',
    sec: 'ref',
    statement: 'statement',
    'supplementary-material': 'supplementary-material',
    table: 'ref',
    'table-fn': 'ref-fn',
  }

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.properties.refType) {
    case 'bibr': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              value: node.children
                .map((node) => {
                  //@ts-ignore
                  const n = node.value.replace(/[\[\], ]/g, '')
                  return n ? `bib${n}` : undefined
                })
                .filter((n) => !!n)
                .join(','),
            },
          ],
        },
      ])
    }
    case 'fig': {
      return j(node, 'command', { name: 'autocite' }, [
        {
          type: 'commandArg',
          children: [
            {
              type: 'text',
              //@ts-expect-error
              value: 'bib' + node.children[0]?.value?.replace(/[\[\]]/g, ''),
            },
          ],
        },

ff00e475ed58bd83515617fa8adbea9a9b199fe0

Clean up delimiter picking for math env

Clean up delimiter picking for math env

https://github.com/JournalOfTrialAndError/JOTE/blob/8611ac9a411c8603513b9ad20bbbb6e9cbcd1a89/libs/ltast-util-to-latex/src/lib/handle/display-math.ts#L12

  options: Options = {}
) => {
  const builtInDelimiters =
    node.delimiters === '[]' ? ['\\[', '\\]'] : ['$$', '$$']

  // TODO: Clean up delimiter picking for math env
  const delimiters = options.inlineMathDelimiters
    ? options.displayMathDelimiters === '[]'
      ? ['\\[', '\\]']
      : ['$$', '$$']
    : builtInDelimiters
  const [left, right] = delimiters
  return `
  ${left}

68839354c3895ae250dae48fbd300f4e5703bcaf

[ooxast-util-to-unified-latex] do something with the styles

[ooxast-util-to-unified-latex] do something with the styles

const styles = select('w\:styles', node)

if (styles) {

h.styles = all(h, styles)

}

// TODO: [ooxast-util-to-unified-latex] do something with the styles

import { all } from '../all'
import { H, Handle } from '../types'
import { Body, Document, Endnotes, Footnotes } from 'ooxast'
import { select } from 'xast-util-select'
import { notes } from '../util/notes'
import { convertElement } from 'xast-util-is-element'

const isFootnotes = convertElement<Footnotes>('w:footnotes')
const isEndnotes = convertElement<Endnotes>('w:endnotes')

export const document: Handle = (h: H, node: Document) => {
  h.simpleParagraph = true
  const body = select('w\\:body', node)

  const foots = select('w\\:footnotes', node)
  if (foots && isFootnotes(foots)) {
    h.footnotes = notes(h, foots)
  }

  const endnotes = select('w\\:endnotes', node)
  if (endnotes && isEndnotes(endnotes)) {
    h.endnotes = notes(h, endnotes)
  }

  // TODO: [ooxast-util-to-unified-latex] do something with the styles
  // const styles = select('w\\:styles', node)
  // if (styles) {
  //   h.styles = all(h, styles)
  // }

  const relations = select('w\\:relationships', node)

51dd9f72ae3918d8eed612857d20153c7db80d31

fix this type error

fix this type error

const id =

node.attributes &&

'id' in node.attributes &&

String(node.attributes.id).toUpperCase()

byId[id] = node

}

})

as there the whitespace matters.

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/ooxast-util-to-jast.ts#L39

import { one } from './one'
import { handlers } from './handlers/index'
import { own } from './util/own'

import {
  Context,
  J,
  JWithoutProps,
  JWithProps,
  JastContent,
  JastRoot,
  Node,
  Options,
  Attributes,
} from './types'
import { convert } from 'unist-util-is'
import rehypeMinifyWhitespace from 'rehype-minify-whitespace'

export { one } from './one'
export { all } from './all'
export { handlers as defaultHandlers }

const block = convert(['heading', 'paragraph', 'root'])

export function toJast(
  tree: JastRoot | JastContent,
  options: Options = {
    newLines: false,
    checked: '[x]',
    unchecked: '[ ]',
    quotes: ['"'],
    topSection: 1,
    columnSeparator: false,
  }
) {
  // const byId: { [s: string]: Element } = {}
  let jast: JastContent | JastRoot

  // TODO: fix this type error
  const j: J = Object.assign(
    ((
      node: JastRoot | JastContent,
      type: string,
      props?: Attributes | string | Array<JastContent>,
      children?: string | Array<JastContent>
    ) => {
      let attributes: Attributes | undefined

      if (typeof props === 'string' || Array.isArray(props)) {
        children = props
        attributes = {}
      } else {
        attributes = props
      }

      // @ts-ignore Assume valid `type` and `children`/`value`.
      const result: Node = { type, ...attributes }

      if (typeof children === 'string') {
        // @ts-ignore: Looks like a literal.
        result.value = children
      } else if (children) {
        // @ts-ignore: Looks like a parent.
        result.children = children
      }

      if (node.position) {
        result.position = node.position
      }

      return result as JastContent
    }) as JWithProps & JWithoutProps,
    {
      //  nodeById: byId,
      baseFound: false,
      inTable: false,
      wrapText: true,
      /** @type {string|null} */
      frozenBaseUrl: null,
      qNesting: 0,
      handlers: options.handlers
        ? { ...handlers, ...options.handlers }
        : handlers,
      document: options.document,
      checked: options.checked || '[x]',
      unchecked: options.unchecked || '[ ]',
      quotes: options.quotes || ['"'],
      italics: options.italics || 'emph',
      sectionDepth: options.topSection || 1,
      documentClass: options.documentClass || { name: 'article' },
      bibname: options.bibname || 'bibliography',
      columnSeparator: !!options.columnSeparator,
    } as Context
  )

  // visit(tree, 'element', (node) => {
  //   const id =
  //     node.attributes &&
  //     'id' in node.attributes &&
  //     String(node.attributes.id).toUpperCase()

  //   if (id && !own.call(byId, id)) {
  //     byId[id] = node
  //   }
  // })

  // @ts-ignore: does return a transformer, that does accept any node.
  rehypeMinifyWhitespace({ newlines: options.newlines === true })(tree)

  // @ts-ignore: does return a transformer, that does accept any node.
  const result = one(j, tree, undefined)

  if (!result) {
    jast = { type: 'root', children: [] }
  } else if (Array.isArray(result)) {
    jast = { type: 'root', children: result }
  } else {
    jast = result
  }

  // visit(mdast, 'text', ontext)

  return jast

  /**
   * Collapse text nodes, and fix whitespace.
   * Most of this is taken care of by `rehype-minify-whitespace`, but
   * weโ€™re generating some whitespace too, and some nodes are in the end
   * ignored.
   * So clean up.
   *
   //* {import('unist-util-visit/complex-types').BuildVisitor JastRoot, 'text'>}
   */
  function ontext(node: any, index: any, parent: any) {
    /* c8 ignore next 3 */
    if (index === null || !parent) {
      return
    }

    const previous = parent.children[index - 1]

    if (previous && previous.type === node.type) {
      previous.value += node.value
      parent.children.splice(index, 1)

      if (previous.position && node.position) {
        previous.position.end = node.position.end
      }

      // Iterate over the previous node again, to handle its total value.
      return index - 1
    }

    node.value = node.value.replace(/[\t ]*(\r?\n|\r)[\t ]*/, '$1')

    // We donโ€™t care about other phrasing nodes in between (e.g., `[ asd ]()`),
    // as there the whitespace matters.
    if (parent && block(parent)) {
      if (!index) {
        node.value = node.value.replace(/^[\t ]+/, '')
      }

      if (index === parent.children.length - 1) {
        node.value = node.value.replace(/[\t ]+$/, '')
      }
    }

    if (!node.value) {
      parent.children.splice(index, 1)
      return index
    }
  }
}

c602797e11525033792ffde227aba6108c16e7c1

[parser] It's currently extremely slow for large sentences, not good.

[parser] It's currently extremely slow for large sentences, not good.

https://github.com/JournalOfTrialAndError/JOTE/blob/59c8623885f0330e9c945306e09214b5fb378d5b/libs/parse-text-cite/src/lib/apa.ts#L35

declare var Lowword: any;
declare var NL: any;
declare var Misc: any;
declare var End: any;
declare var Et: any;
declare var Low: any;

import {lexer} from './lexer'

// TODO: [parser] It's currently extremely slow for large sentences, not good.
const getFullName = (name: {family:string,
                            'non-dropping-particle':string
                           }

5ffb18f5b213a62de92ebb915f8928f6e40116bd

For some reason ts expect error doesn't work for unknowntype

For some reason ts expect error doesn't work for unknowntype

https://github.com/JournalOfTrialAndError/JOTE/blob/4b292b06dc554e7a6e4e5bdce792f5a57d0bef81/libs/xast/xast-util-select/src/lib/any.ts#L20

import { test } from './test'

const type = zwitch('type', {
  /**
   * TODO: For some reason ts expect error doesn't work for unknowntype
   */
  unknown: unknownType as any ,
  invalid: invalidType,
  handlers: { selectors, ruleSet, rule } as any,
})

/**

a6618edceb94af2128c9eb14b758d6caafffb2a9

[rejour-relatex] make latex cite command setting more modular and customizable

[rejour-relatex] make latex cite command setting more modular and customizable

https://github.com/JournalOfTrialAndError/JOTE/blob/cf52c8d4e0e45a1364ad7be39ca535593835c3ff/libs/rejour/jast-util-to-texast/src/lib/handlers/xref.ts#L52

  // TODO: [rejour-rehype/citations] make citation parsing less hardcoded
  // Maybe add a new type to texast: citation.

  const labelToText: { [key: string]: string } = {
    page: 'pp.',
    appendix: 'App.',
  }

  // TODO: [rejour-rehype/citations] make checks for the kind of citations used.
  switch (node.attributes.refType) {
    case 'bibr': {
      const customType = node.attributes.customType

      // TODO: [rejour-relatex] make latex cite command setting more modular and customizable
      let command
      let pre
      let post
      if (customType) {
        const customData = JSON.parse(customType)
        const { prefix, infix, label, locator, mode, suffix } = customData

        const pref = (mode ? infix : prefix) || ''

        const suff = `${
          label && label !== 'none'
            ? `${labelToText[label] || label || 'pp.'} `
            : ''
        }${locator || ''}`
        command = mode ? 'textcite' : 'parencite'

        if (pref) pre = pref
        if (suff) post = suff
      }

      const optCommandArgs = createOptCiteArgs(pre, post)
      return j(
        node,
        'command',
        { name: command || j.citationAnalyzer(node) || 'autocite' },
        [
          ...optCommandArgs,
          {
            type: 'commandArg',
            children: [

c0fa605e4d86bc398d0d0222c8f6f38059efad35

hard-coded "src"

hard-coded "src"

import type { ExecutorContext, ProjectGraphProjectNode } from '@nrwl/devkit';
import { normalizePath, readJsonFile } from '@nrwl/devkit';
import {
  copySync,
  readdirSync,
  readFileSync,
  removeSync,
  writeFileSync,
} from 'fs-extra';
import { join, relative } from 'path';
import type { NormalizedExecutorOptions } from '../executors/tsc/schema';
import { existsSync } from 'fs';

interface InlineProjectNode {
  name: string;
  root: string;
  sourceRoot: string;
  pathAlias: string;
  buildOutputPath?: string;
}

export interface InlineProjectGraph {
  nodes: Record<string, InlineProjectNode>;
  externals: Record<string, InlineProjectNode>;
  dependencies: Record<string, string[]>;
}

export function isInlineGraphEmpty(inlineGraph: InlineProjectGraph): boolean {
  return Object.keys(inlineGraph.nodes).length === 0;
}

export function handleInliningBuild(
  context: ExecutorContext,
  options: NormalizedExecutorOptions,
  tsConfigPath: string
): InlineProjectGraph {
  const tsConfigJson = readJsonFile(tsConfigPath);
  const pathAliases =
    tsConfigJson['compilerOptions']['paths'] || readBasePathAliases(context);
  const inlineGraph = createInlineGraph(context, options, pathAliases);

  if (isInlineGraphEmpty(inlineGraph)) {
    return inlineGraph;
  }

  buildInlineGraphExternals(context, inlineGraph, pathAliases);

  return inlineGraph;
}

export function postProcessInlinedDependencies(
  outputPath: string,
  parentOutputPath: string,
  inlineGraph: InlineProjectGraph
) {
  if (isInlineGraphEmpty(inlineGraph)) {
    return;
  }

  const parentDistPath = join(outputPath, parentOutputPath);

  // move parentOutput
  movePackage(parentDistPath, outputPath);

  const inlinedDepsDestOutputRecord: Record<string, string> = {};
  // move inlined outputs

  for (const inlineDependenciesNames of Object.values(
    inlineGraph.dependencies
  )) {
    for (const inlineDependenciesName of inlineDependenciesNames) {
      const inlineDependency = inlineGraph.nodes[inlineDependenciesName];
      const depOutputPath =
        inlineDependency.buildOutputPath ||
        join(outputPath, inlineDependency.root);
      const destDepOutputPath = join(outputPath, inlineDependency.name);
      const isBuildable = !!inlineDependency.buildOutputPath;

      if (isBuildable) {
        copySync(depOutputPath, destDepOutputPath, { overwrite: true });
      } else {
        movePackage(depOutputPath, destDepOutputPath);
      }

      // TODO: hard-coded "src"
      inlinedDepsDestOutputRecord[inlineDependency.pathAlias] =
        destDepOutputPath + '/src';
    }
  }

  updateImports(outputPath, inlinedDepsDestOutputRecord);
}

function readBasePathAliases(context: ExecutorContext) {
  return readJsonFile(getRootTsConfigPath(context))?.['compilerOptions'][
    'paths'
  ];
}

export function getRootTsConfigPath(context: ExecutorContext): string | null {
  for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) {
    const tsConfigPath = join(context.root, tsConfigName);
    if (existsSync(tsConfigPath)) {
      return tsConfigPath;
    }
  }

  throw new Error(
    'Could not find a root tsconfig.json or tsconfig.base.json file.'
  );
}

function emptyInlineGraph(): InlineProjectGraph {
  return { nodes: {}, externals: {}, dependencies: {} };
}

function projectNodeToInlineProjectNode(
  projectNode: ProjectGraphProjectNode,
  pathAlias = '',
  buildOutputPath = ''
): InlineProjectNode {
  return {
    name: projectNode.name,
    root: projectNode.data.root,
    sourceRoot: projectNode.data.sourceRoot,
    pathAlias,
    buildOutputPath,
  };
}

function createInlineGraph(
  context: ExecutorContext,
  options: NormalizedExecutorOptions,
  pathAliases: Record<string, string[]>,
  projectName: string = context.projectName,
  inlineGraph: InlineProjectGraph = emptyInlineGraph()
) {
  if (options.external == null && options.internal == null) return inlineGraph;

  const projectDependencies =
    context.projectGraph.dependencies[projectName] || [];

  if (projectDependencies.length === 0) return inlineGraph;

  if (!inlineGraph.nodes[projectName]) {
    inlineGraph.nodes[projectName] = projectNodeToInlineProjectNode(
      context.projectGraph.nodes[projectName]
    );
  }

  const implicitDependencies =
    context.projectGraph.nodes[projectName].data.implicitDependencies || [];

  for (const projectDependency of projectDependencies) {
    // skip npm packages
    if (projectDependency.target.startsWith('npm')) {
      continue;
    }

    // skip implicitDependencies
    if (implicitDependencies.includes(projectDependency.target)) {
      continue;
    }

    const pathAlias = getPathAliasForPackage(
      context.projectGraph.nodes[projectDependency.target],
      pathAliases
    );

    const buildOutputPath = getBuildOutputPath(
      projectDependency.target,
      context,
      options
    );

    const shouldInline =
      /**
       * if some buildable libraries are marked as internal,
       */
      options.internal?.includes(projectDependency.target) ||
      /**
       * if all buildable libraries are marked as external,
       * then push the project dependency that doesn't have a build target
       */
      (options.external === 'all' && !buildOutputPath) ||
      /**
       * if all buildable libraries are marked as internal,
       * then push every project dependency to be inlined
       */
      options.external === 'none' ||
      /**
       * if some buildable libraries are marked as external,
       * then push the project dependency that IS NOT marked as external OR doesn't have a build target
       */
      (Array.isArray(options.external) &&
        options.external.length > 0 &&
        !options.external.includes(projectDependency.target)) ||
      !buildOutputPath;

    console.log(shouldInline, projectDependency.target);
    if (shouldInline) {
      inlineGraph.dependencies[projectName] ??= [];
      inlineGraph.dependencies[projectName].push(projectDependency.target);
    }

    inlineGraph.nodes[projectDependency.target] =
      projectNodeToInlineProjectNode(
        context.projectGraph.nodes[projectDependency.target],
        pathAlias,
        buildOutputPath
      );

    if (
      context.projectGraph.dependencies[projectDependency.target].length > 0
    ) {
      inlineGraph = createInlineGraph(
        context,
        options,
        pathAliases,
        projectDependency.target,
        inlineGraph
      );
    }
  }

  return inlineGraph;
}

function buildInlineGraphExternals(
  context: ExecutorContext,
  inlineProjectGraph: InlineProjectGraph,
  pathAliases: Record<string, string[]>
) {
  const allNodes = { ...context.projectGraph.nodes };

  for (const [parent, dependencies] of Object.entries(
    inlineProjectGraph.dependencies
  )) {
    if (allNodes[parent]) {
      delete allNodes[parent];
    }

    for (const dependencyName of dependencies) {
      const dependencyNode = inlineProjectGraph.nodes[dependencyName];

      // buildable is still external even if it is a dependency
      if (dependencyNode.buildOutputPath) {
        continue;
      }

      if (allNodes[dependencyName]) {
        delete allNodes[dependencyName];
      }
    }
  }

  for (const [projectName, projectNode] of Object.entries(allNodes)) {
    if (!inlineProjectGraph.externals[projectName]) {
      inlineProjectGraph.externals[projectName] =
        projectNodeToInlineProjectNode(
          projectNode,
          getPathAliasForPackage(projectNode, pathAliases)
        );
    }
  }
}

function movePackage(from: string, to: string) {
  if (from === to) return;
  copySync(from, to, { overwrite: true });
  removeSync(from);
}

function updateImports(
  destOutputPath: string,
  inlinedDepsDestOutputRecord: Record<string, string>
) {
  const importRegex = new RegExp(
    Object.keys(inlinedDepsDestOutputRecord)
      .map((pathAlias) => `["'](${pathAlias})["']`)
      .join('|'),
    'g'
  );
  recursiveUpdateImport(
    destOutputPath,
    importRegex,
    inlinedDepsDestOutputRecord
  );
}

function recursiveUpdateImport(
  dirPath: string,
  importRegex: RegExp,
  inlinedDepsDestOutputRecord: Record<string, string>,
  rootParentDir?: string
) {
  const files = readdirSync(dirPath, { withFileTypes: true });
  for (const file of files) {
    // only check .js and .d.ts files
    if (
      file.isFile() &&
      (file.name.endsWith('.js') || file.name.endsWith('.d.ts'))
    ) {
      const filePath = join(dirPath, file.name);
      const fileContent = readFileSync(filePath, 'utf-8');
      const updatedContent = fileContent.replace(importRegex, (matched) => {
        const result = matched.replace(/['"]/g, '');
        // If a match is the same as the rootParentDir, we're checking its own files so we return the matched as in no changes.
        if (result === rootParentDir) return matched;
        const importPath = `"${relative(
          dirPath,
          inlinedDepsDestOutputRecord[result]
        )}"`;
        return normalizePath(importPath);
      });
      writeFileSync(filePath, updatedContent);
    } else if (file.isDirectory()) {
      recursiveUpdateImport(
        join(dirPath, file.name),
        importRegex,
        inlinedDepsDestOutputRecord,
        rootParentDir || file.name
      );
    }
  }
}

function getPathAliasForPackage(
  packageNode: ProjectGraphProjectNode,
  pathAliases: Record<string, string[]>
): string {
  if (!packageNode) return '';

  for (const [alias, paths] of Object.entries(pathAliases)) {
    if (paths.some((path) => path.includes(packageNode.data.root))) {
      return alias;
    }
  }

  return '';
}

function getBuildOutputPath(
  projectName: string,
  context: ExecutorContext,
  options: NormalizedExecutorOptions
): string {
  const projectTargets = context.projectGraph.nodes[projectName]?.data?.targets;
  if (!projectTargets) return '';

  const buildTarget = options.externalBuildTargets.find(
    (buildTarget) => projectTargets[buildTarget]
  );

  return buildTarget ? projectTargets[buildTarget].options['outputPath'] : '';
}

d1358f63ec92179924a0595d64a6c9a0a8228723

Add checks to preamble to not print non-command content

Add checks to preamble to not print non-command content

https://github.com/JournalOfTrialAndError/JOTE/blob/6e4fc27aa1d2601f05999b47dca861e20a5bbd3d/libs/ltast-util-to-latex/src/lib/handle/preamble.ts#L9

import { Preamble } from 'relatex'
import { toLatex } from '../ltast-util-to-latex'
import { BasicHandle, Handle, Options } from '../types'

export const preamble: BasicHandle = (
  node: Preamble,
  options: Options = {}
) => {
  // TODO: Add checks to preamble to not print non-command content
  return toLatex(node.children)
}

7c161879c84dbb2b5e7903779f5d58e3195fcba7

remove unnecssary properties first, that way this will equal more often

remove unnecssary properties first, that way this will equal more often

string check otherwise === don't work

https://github.com/JournalOfTrialAndError/JOTE/blob/59c8623885f0330e9c945306e09214b5fb378d5b/libs/ooxast/ooxast-util-remove-rsid/src/lib/ooxast-util-remove-rsid.ts#L71

import { visit } from '@jote/utils'
import { Text, Root, Node, P, R } from 'ooxast'
import { convertElement, isElement } from 'xast-util-is-element'
import { select } from 'xast-util-select'
import { removePosition } from 'unist-util-remove-position'
import { select as unistSelect } from 'unist-util-select'
import { remove } from 'unist-util-remove'

// Check to see if node is a paragraph, because we want to merge elements in a paragraph
const isP = convertElement<P>('w:p')
const isR = convertElement<R>('w:r')

export interface Options {
  rPrRemoveList?: string[]
}

export function ooxastUtilRemoveRsid(tree: Root, options?: Options): Root
export function ooxastUtilRemoveRsid(tree: Node, options?: Options): Node
export function ooxastUtilRemoveRsid(
  tree: Root | Node,
  options?: Options
): Root | Node {
  visit(tree, isP, (node: P) => {
    // Clean rsid props from P
    const {
      'w:rsidR': rr,
      'w:rsidRPr': rpr,
      'w:rsidRDefault': rd,
      'w:rsidP': rp,
      ...rest
    } = node.attributes

    node.attributes = rest as any

    const kids = node.children
    const runs: typeof kids = []

    for (const kid of kids) {
      let lastEl = runs[runs.length - 1]

      if (!isR(kid)) {
        runs.push(kid)
        continue
      }
      let el = removeRsid(kid)

      el = maybeRemoveRunProperties(el, options?.rPrRemoveList)

      if (lastEl?.name !== 'w:r') {
        runs.push(el)
        continue
      }

      const lastT = select('w\\:t', lastEl)
      const t = select('w\\:t', el)
      // If either one of them don't have text, don't merge them ya dummy
      if (!lastT || !t) {
        runs.push(el)
        continue
      }

      const pr = select('w\\:rPr', el)
      const lastRPr = select('w\\:rPr', lastEl)
      //null case
      if (!lastRPr && !pr) {
        lastEl = merge(lastEl, el)
        continue
      }

      // one of them has props while the other doesn't, don't merge
      // TODO: remove unnecssary properties first, that way this will equal more often
      if ((!lastRPr && pr) || (lastRPr && !pr)) {
        runs.push(el)
        continue
      }

      // they both have properties but they differ, e.g. one is italic, the other bold. Should not merge
      // string check otherwise === don't work
      if (
        JSON.stringify(removePosition(lastRPr!, true)) !==
        JSON.stringify(removePosition(pr!, true))
      ) {
        runs.push(el)
        continue
      }

      lastEl = merge(lastEl, el)
    }

    node.children = runs
  })
  return tree
}

function maybeRemoveRunProperties(r: R, options: string[] | undefined): R {
  if (!options) {
    return r
  }

  remove(r, (element) => isElement(element) && options.includes(element.name))
  return r
}

/**
 * Merge two runs into an old one by concatenating the text properties.
 * If they don't have text, don't merge them.
 */
function merge(prev: R, curr: R): R {
  const lastText = unistSelect('text', prev) as Text
  const text = unistSelect('text', curr) as Text

  if (!lastText) {
    return curr
  }

  if (!text) {
    return prev
  }

  lastText.value += text.value
  return prev
}

function removeRsid(node: R): R {
  const { 'w:rsidRPr': rp, 'w:rsidR': r, ...rest } = node.attributes
  node.attributes = rest as any
  return node
}

cf0a6ee32e0f85798ed5ff674890033d2ad76cfd

[jast-util-to-texast] Store information about column alignment in tabular

[jast-util-to-texast] Store information about column alignment in tabular

https://github.com/JournalOfTrialAndError/JOTE/blob/131a791996b998f95b72e1cc839ab710bb021681/libs/rejour/jast-util-to-texast/src/lib/handlers/table.ts#L16

// based on https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/em

import { Table, isElement, Element } from 'jjast'
import { all } from '../all'
import { J, Node } from '../types'
import { visit as origVisit } from 'unist-util-visit'
import { CommandArg } from 'texast'

// try to turn of typechecking for visit as it it bugged
// https://github.com/syntax-tree/unist-util-visit/issues/33
const visit = origVisit as any
export function table(j: J, table: Table) {
  let numberOfColumns = 0
  let hasCols = false
  // tables can be def'd in terms of cols or rows
  // TODO: [jast-util-to-texast] Store information about column alignment in tabular
  visit(
    table,
    (node: Node) => isElement(node) && ['col', 'tr'].includes(node.tagName),
    (node: Element) => {
      if (node.tagName === 'col') {
        hasCols = true
        numberOfColumns++
        return
      }

      if (hasCols) return
      visit(node, 'td', (td: Node) => {
        numberOfColumns++
      })
      hasCols = true
    }
  )

  const colAlignment = new Array(numberOfColumns).map((col) => 'c').join(' | ')
  const colAlignArg: CommandArg = {
    type: 'commandArg',
    children: [{ type: 'text', value: colAlignment }],
  }

  const contents = all(j, table)
  contents.unshift(colAlignArg)

  return { type: 'environment', name: 'tabular', children: contents }
}

aada405ae57440e1136dbc840d7c870c25dc9c89

Clean up delimiter picking for math env

Clean up delimiter picking for math env

https://github.com/JournalOfTrialAndError/JOTE/blob/8611ac9a411c8603513b9ad20bbbb6e9cbcd1a89/libs/ltast-util-to-latex/src/lib/handle/inline-math.ts#L13

  options: Options = {}
) => {
  const builtInDelimiters =
    node.delimiters === '()' ? ['\\(', '\\)'] : ['$', '$']
  console.log(node.delimiters)

  // TODO: Clean up delimiter picking for math env
  const delimiters = options.inlineMathDelimiters
    ? options.inlineMathDelimiters === '()'
      ? ['\\(', '\\)']
      : ['$', '$']
    : builtInDelimiters
  const [left, right] = delimiters
  return ` ${left} ${toLatex(node.children)} ${right} `
}

48d21aea7cc09e01d09c08543b7f46ebb96f29b2

[csl-to-jts] add editor support

[csl-to-jts] add editor support

https://github.com/JournalOfTrialAndError/JOTE/blob/4b14de45df7a9618fe579a3052560971317727ef/libs/rejour/csl-to-jast/src/lib/csl-to-jast.ts#L169

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  ElementCitation,
  Publisher,
  PubId,
  PersonGroup,
  Source,
  Date,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jjast'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

// @ts-ignore
export function cslToFront(data: CSL): Front {}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-ignore no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-ignore
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? //@ts-ignore
      x('personGroup', { personGroupType: 'author' }, names)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

ebb5663f38b5f50276da3499c2afcdae7fb0d9b4

Why doesnt TS-expect-error work reee

Why doesnt TS-expect-error work reee

https://github.com/JournalOfTrialAndError/JOTE/blob/4b292b06dc554e7a6e4e5bdce792f5a57d0bef81/libs/xast/xast-util-select/src/lib/attribute.ts#L18

import { zwitch } from 'zwitch'

const handle = zwitch('operator', {
  /**
   * TODO: Why doesnt TS-expect-error work reee
   */
  //// @ts-expect-error: hush.
  unknown: unknownOperator as  any,
  //// @ts-expect-error: hush.
  invalid: exists as any,
  handlers: {
    '=': exact as any,
    '~=': spaceSeparatedList as any,
    '|=': exactOrPrefix as any,
    '^=': begins as any,
    '$=': ends as any,
    '*=': contains as any,
  },
})

80bcb1c0f9e4f44b5784cfac8fa59404320506ca

somehow types don't align, fix

somehow types don't align, fix

https://github.com/JournalOfTrialAndError/JOTE/blob/b3f55b145db6127b88ab8c195e3ca988dc4ee5bb/libs/reoff/reoff-parse/src/lib/reoff-parse.ts#L49

import { fromXml } from 'xast-util-from-xml'

import { ParserFunction } from 'unified'
import { Root, Node as XastNode, Element as XastElement } from 'xast'
import { filter } from 'unist-util-filter'
import { map } from 'unist-util-map'

export interface Settings {
  removeWhiteSpace?: boolean
  fragment?: boolean
}

export default function reoffParse(options: Settings = {}) {
  const parser: ParserFunction<Root> = (doc) => {
    // Assume options.
    const settings: Settings = this.data('settings')

    const configuration = Object.assign({}, settings, options, {
      // Note: these options are not in the readme.
      // The goal is for them to be set by plugins on `data` instead of being
      // passed by users.
      //extensions: this.data('micromarkExtensions') || [],
      //mdastExtensions: this.data('fromMarkdownExtensions') || [],
    })

    const treeify = (doc: string) => {
      try {
        return fromXml(doc)
      } catch (e) {
        console.error(e)
        throw e
      }
    }
    let tree = treeify(doc)

    tree = settings?.removeWhiteSpace
      ? filter(tree, (node: XastNode) => {
          return !(
            //@ts-ignore ITS FINE
            (node.type === 'text' && node.value.replace(/[\n ]+/, '') === '')
          )
        })!
      : tree

    // map
    // attributes --> attributes
    // name --> name
    // to be more in line with hast, which makes plugins easier to port
    //@ts-ignore: TODO:somehow types don't align, fix
    tree = map(tree!, (node) => {
      if (node.type !== 'element') return node
      const element = node as XastElement

      const attributes = element.attributes
        ? Object.entries(element.attributes).reduce(
            (
              acc: { [key: string]: any },
              [key, value]: [key: string, value: any]
            ) => {
              acc[pascalToCamelCase(key)] = value
              return acc
            },
            {}
          )
        : {}
      return {
        type: 'element',
        name: pascalToCamelCase(element.name),
        attributes: attributes,
        children: element.children,
        ...(element.position ? { position: element.position } : {}),
      }
    })

    return tree as Root
  }

  Object.assign(this, { Parser: parser })
}

/**
 * Turn a pascal-case string into a camel-case string.
 * Necessary because working with pascal-case in js is annoying.
 */
function pascalToCamelCase(input: string): string {
  return input.replace(/-(\w)/g, (string, lowercaseLetter) =>
    lowercaseLetter.toUpperCase()
  )
}

6c47e029a5a5855eced74453da0a64d51ed0bd9a

[csl-to-jast] write a function that converts CSL to JAST frontmatter

[csl-to-jast] write a function that converts CSL to JAST frontmatter

'date',

[

) as Date,

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-from-csl/src/lib/csl-to-jast.ts#L28

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  PubId,
  PersonGroup,
  Source,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jast-types'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-expect-error no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-expect-error I can assign it to this dw bby
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? (x('personGroup', { personGroupType: 'author' }, names) as PersonGroup)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

b11324e1bde0b28484f7fbcd340089dab7cb44ac

put back when inlining story is more stable

put back when inlining story is more stable

if (options.external == null) {

options.external = 'all';

} else if (Array.isArray(options.external) && options.external.length === 0) {

options.external = 'none';

}

// TODO: put back when inlining story is more stable

import { ExecutorContext } from '@nrwl/devkit';
import {
  assetGlobsToFiles,
  FileInputOutput,
} from '@nrwl/workspace/src/utilities/assets';
import type { TypeScriptCompilationOptions } from '@nrwl/workspace/src/utilities/typescript/compilation';
import { join, resolve } from 'path';
import {
  CustomTransformers,
  Program,
  SourceFile,
  TransformerFactory,
} from 'typescript';
import { CopyAssetsHandler } from '@nrwl/js/src/utils/assets/copy-assets-handler';
import { checkDependencies } from '../../utils/check-dependencies';
import {
  getHelperDependency,
  HelperDependency,
} from '@nrwl/js/src/utils/compiler-helper-dependency';
import {
  handleInliningBuild,
  isInlineGraphEmpty,
  postProcessInlinedDependencies,
} from '../../utils/inline';
import { updatePackageJson } from '../../utils/package-json/update-package-json';
import { ExecutorOptions, NormalizedExecutorOptions } from './schema';
import { compileTypeScriptFiles } from '@nrwl/js/src/utils/typescript/compile-typescript-files';
import { loadTsTransformers } from '@nrwl/js/src/utils/typescript/load-ts-transformers';
import { watchForSingleFileChanges } from '@nrwl/js/src/utils/watch-for-single-file-changes';

export function normalizeOptions(
  options: ExecutorOptions,
  contextRoot: string,
  sourceRoot: string,
  projectRoot: string
): NormalizedExecutorOptions {
  const outputPath = join(contextRoot, options.outputPath);
  const rootDir = options.rootDir
    ? join(contextRoot, options.rootDir)
    : projectRoot;

  if (options.watch == null) {
    options.watch = false;
  }

  // TODO: put back when inlining story is more stable
  // if (options.external == null) {
  //   options.external = 'all';
  // } else if (Array.isArray(options.external) && options.external.length === 0) {
  //   options.external = 'none';
  // }

  if (Array.isArray(options.external) && options.external.length > 0) {
    const firstItem = options.external[0];
    if (firstItem === 'all' || firstItem === 'none') {
      options.external = firstItem;
    }
  }

  const files: FileInputOutput[] = assetGlobsToFiles(
    options.assets,
    contextRoot,
    outputPath
  );

  return {
    ...options,
    root: contextRoot,
    sourceRoot,
    projectRoot,
    files,
    outputPath,
    tsConfig: join(contextRoot, options.tsConfig),
    rootDir,
    mainOutputPath: resolve(
      outputPath,
      options.main.replace(`${projectRoot}/`, '').replace('.ts', '.js')
    ),
  };
}

export function createTypeScriptCompilationOptions(
  normalizedOptions: NormalizedExecutorOptions,
  context: ExecutorContext
): TypeScriptCompilationOptions {
  const { compilerPluginHooks } = loadTsTransformers(
    normalizedOptions.transformers
  );
  const getCustomTransformers = (program: Program): CustomTransformers => ({
    before: compilerPluginHooks.beforeHooks.map(
      (hook) => hook(program) as TransformerFactory<SourceFile>
    ),
    after: compilerPluginHooks.afterHooks.map(
      (hook) => hook(program) as TransformerFactory<SourceFile>
    ),
    afterDeclarations: compilerPluginHooks.afterDeclarationsHooks.map(
      (hook) => hook(program) as TransformerFactory<SourceFile>
    ),
  });

  return {
    outputPath: normalizedOptions.outputPath,
    projectName: context.projectName,
    projectRoot: normalizedOptions.projectRoot,
    rootDir: normalizedOptions.rootDir,
    tsConfig: normalizedOptions.tsConfig,
    watch: normalizedOptions.watch,
    deleteOutputPath: normalizedOptions.clean,
    getCustomTransformers,
  };
}

export async function* tscExecutor(
  _options: ExecutorOptions,
  context: ExecutorContext
) {
  const { sourceRoot, root } =
    context.projectsConfigurations.projects[context.projectName];
  const options = normalizeOptions(_options, context.root, sourceRoot, root);

  const { projectRoot, tmpTsConfig, target, dependencies } = checkDependencies(
    context,
    _options.tsConfig
  );

  if (tmpTsConfig) {
    options.tsConfig = tmpTsConfig;
  }

  const tsLibDependency = getHelperDependency(
    HelperDependency.tsc,
    options.tsConfig,
    dependencies,
    context.projectGraph
  );

  if (tsLibDependency) {
    dependencies.push(tsLibDependency);
  }

  const assetHandler = new CopyAssetsHandler({
    projectDir: projectRoot,
    rootDir: context.root,
    outputDir: _options.outputPath,
    assets: _options.assets,
  });

  const tsCompilationOptions = createTypeScriptCompilationOptions(
    options,
    context
  );

  const inlineProjectGraph = handleInliningBuild(
    context,
    options,
    tsCompilationOptions.tsConfig
  );

  if (!isInlineGraphEmpty(inlineProjectGraph)) {
    tsCompilationOptions.rootDir = '.';
  }

  const typescriptCompilation = compileTypeScriptFiles(
    options,
    tsCompilationOptions,
    async () => {
      await assetHandler.processAllAssetsOnce();
      updatePackageJson(options, context, target, dependencies);

      postProcessInlinedDependencies(
        tsCompilationOptions.outputPath,
        tsCompilationOptions.projectRoot,
        inlineProjectGraph
      );
    }
  );

  if (options.watch) {
    const disposeWatchAssetChanges =
      await assetHandler.watchAndProcessOnAssetChange();
    const disposePackageJsonChanges = await watchForSingleFileChanges(
      context.projectName,
      options.projectRoot,
      'package.json',
      () => updatePackageJson(options, context, target, dependencies)
    );
    const handleTermination = async (exitCode: number) => {
      await typescriptCompilation.close();
      disposeWatchAssetChanges();
      disposePackageJsonChanges();
      process.exit(exitCode);
    };
    process.on('SIGINT', () => handleTermination(128 + 2));
    process.on('SIGTERM', () => handleTermination(128 + 15));
  }

  return yield* typescriptCompilation.iterator;
}

export default tscExecutor;

f800a730dac11e11275c1fff08e86a8bd3e0f9de

[csl-to-jts] add editor support

[csl-to-jts] add editor support

https://github.com/JournalOfTrialAndError/JOTE/blob/60209cf25a2f58fc5d46a2f4d88df4c37a9115d5/libs/jast/jast-util-from-csl/src/lib/csl-to-jast.ts#L167

import { Data as CSL } from 'csl-json'
import {
  Ref,
  RefList,
  Front,
  PubId,
  PersonGroup,
  Source,
  Issn,
  Isbn,
  ExtLink,
  Issue,
  Volume,
  Edition,
  PublisherLoc,
  PublisherName,
} from 'jast-types'
import { x } from 'xastscript'
export function cslToJats(data: CSL | CSL[]) {
  if (Array.isArray(data)) {
    return cslToRefList(data)
  }
  return cslToFront(data)
}

//
export function cslToFront(data: CSL) {
  //TODO: [csl-to-jast] write a function that converts CSL to JAST frontmatter
}

export function cslToRefList(
  data: CSL[] | { [key: string | number]: CSL }
): RefList {
  if (Array.isArray(data)) {
    const reflist = data.map((csl, index) => cslToRef(csl, index))
    return x('refList', reflist) as RefList
  }
  const reflist = Object.entries(data).map(([index, csl]) =>
    cslToRef(csl, index)
  )
  return x('refList', reflist) as RefList
}

export function cslToRef(data: CSL, index: number | string): Ref {
  const date = data.issued?.['date-parts']?.[0]
  const [year, month, day] = date || data.issued?.literal?.split('-') || []

  const pubIds = ['DOI', 'PMID', 'PMCID'].flatMap(
    //@ts-expect-error no idea why not work
    (id: 'DOI' | 'PMID' | 'PMCID') =>
      data[id]
        ? //@ts-expect-error I can assign it to this dw bby
          (x('pubId', { pubIdType: id.toLowerCase() }, [
            { type: 'text', value: data[id] },
          ]) as PubId)
        : []
  )

  const names = data.author?.map((person) => {
    return x(
      'name',
      Object.entries(person).flatMap(
        ([name, val]: [name: string, val: string]) => {
          switch (name) {
            case 'family':
              return nameMap('surname', val)
            case 'given':
              return nameMap('givenNames', val)
            case 'suffix':
              return nameMap('suffix', val)
            case 'dropping-particle':
              return nameMap('prefix', val)
            case 'non-dropping-particle':
              return nameMap('prefix', val)
            default:
              return []
          }
        }
      )
    )
  })

  const authors = names
    ? (x('personGroup', { personGroupType: 'author' }, names) as PersonGroup)
    : []

  const pages = getPages(data)
  const source = getTitle(data)

  const elementCitationChildren = [
    nameMap('publisherLoc', data['publisher-place']) as PublisherLoc,
    nameMap('publisherName', data['publisher']) as PublisherName,
    //x(
    //  'date',
    //  [
    nameMap('year', `${year || ''}`),
    nameMap('month', `${month || ''}`),
    nameMap('day', `${day || ''}`),
    //   ].flat()
    // ) as Date,
    pubIds as PubId[],
    authors,
    pages,
    source as Source,
    nameMap('source', data['container-title']) as Source,
    nameMap('issn', data['ISSN']) as Issn,
    nameMap('isbn', data['ISBN']) as Isbn,
    nameMap('extLink', data['URL']) as ExtLink,
    nameMap('issue', `${data['issue'] || data['number'] || ''}`) as Issue,
    nameMap('volume', `${data['volume'] || ''}`) as Volume,
    nameMap('edition', `${data['edition'] || ''}`) as Edition,
  ].flat()

  return x('ref', { id: typeof index === 'string' ? index : `bib${index}` }, [
    x(
      'elementCitation',
      { publicationType: getPublicationType(data) },
      elementCitationChildren
    ),
  ]) as Ref
}
function nameMap(name: string, value: string | undefined) {
  return value ? x(name, [{ type: 'text', value: value }]) : []
}

function getPages(data: CSL) {
  if (data.page) {
    return nameMap('pageRange', data.page)
  } else if (data['page-first']) {
    return nameMap('pageFirst', data['page-first'])
  }
  return []
}

function getTitle(data: CSL) {
  if (!data.title) return []
  if (data.type === 'book') {
    return nameMap('source', data.title)
  }
  return nameMap('articleTitle', data.title)
}

function getPublicationType(data: CSL) {
  switch (data.type) {
    case 'article':
    case 'article-journal':
    case 'article-magazine':
    case 'article-newspaper':
      return 'journal'
    case 'book':
      return 'book'
    case 'chapter':
      return 'bookchapter'
    case 'dataset':
      return 'dataset'
    case 'patent':
      return 'patent'
    case 'review':
      return 'review'
    default:
      return 'standard'
  }
}

// TODO: [csl-to-jts] add reviewer support
// TODO: [csl-to-jts] do something with abstract maybe
// TODO: [csl-to-jts] add editor support
// TODO: [csl-to-jts] use citation key if available

b0215842cccbd798c3c5b59c299104f10c2fe7b3

Make this not error

Make this not error

https://github.com/JournalOfTrialAndError/JOTE/blob/5f8ab764a09da5debb4200ac3a996ced2ca2bbf4/libs/ooxast/ooxast-util-to-jast/src/lib/types.ts#L92

import { Node as UnistNode, Parent as UnistParent } from 'unist'

import {
  Parent as JastParent,
  Content as JastContent,
  Root as JastRoot,
  P,
} from 'jjast'
type JastParagraphContent = P['children'][number]

import {
  Attributes as OoxastProperties,
  //Node,
  Parent,
  Element,
  Text,
  Root,
} from 'ooxast'

/**
 * ooxast Node
 */
export type Node = Parent['children'][number] | Root
type t = Extract<Node, { name: 'td' }>

export type Attributes = OoxastProperties
export interface Options {
  handlers?: { [handle: string]: Handle }
  document?: boolean
  newLines?: boolean
  checked?: string
  unchecked?: string
  quotes?: Array<string>
  topSection?: number
  italics?: 'emph' | 'textit'
  bibname?: string
  columnSeparator?: boolean
  documentClass?: {
    options?: string[]
    name: string
  }
}

export type Handle = (
  j: J,
  node: any,
  parent?: Parent
) => JastContent | Array<JastContent> | void

export interface Context {
  nodeById?: {
    [id: string]: Element
  }
  baseFound: boolean
  frozenBaseUrl: string | null
  wrapText: boolean
  inTable: boolean
  qNesting: number
  handlers: { [handler: string]: Handle }
  document: boolean | undefined
  checked: string
  unchecked: string
  quotes: Array<string>
  italics: string
  documentClass: {
    options?: string[]
    name: string
  }
  sectionDepth: number
  bibname: string
  columnSeparator: boolean
}

export type JWithProps = (
  node: Node,
  type: string,
  props?: Attributes,
  children?: string | Array<JastContent>
) => JastContent

export type JWithoutProps = (
  node: Node,
  type: string,
  children?: string | Array<JastContent>
) => JastContent

export type JWithPropsSpecific<TNode extends JastContent = JastContent> = (
  node: Node,
  type: Pick<TNode, 'type'>,
  props?: Attributes,
  //@ts-ignore yeah i know butttt
  // TODO: Make this not error
  children?: Pick<TNode, 'children'>
) => TNode

export type J = JWithProps & JWithoutProps & Context

export type {
  Parent,
  Root,
  Element,
  JastContent,
  JastParent,
  JastRoot,
  JastParagraphContent,
  Text,
}

export type Parents = Extract<Exclude<Node, Text | Root>, { children: any[] }>

eadee25336c16cc53eeed4517489c2142d98727f

[parser] It's currently extremely slow for large sentences, not good.

[parser] It's currently extremely slow for large sentences, not good.

https://github.com/JournalOfTrialAndError/JOTE/blob/55a3e6a91d55839d39ce0113a51253a56f9408dd/libs/citations/parse-text-cite/src/lib/apa.ts#L37

// Generated automatically by nearley, version 2.20.1
// http://github.com/Hardmath123/nearley
// Bypasses TS6133. Allow declared but unused functions.
// @ts-ignore
function id(d: any[]): any {
  return d[0]
}
declare var Year: any
declare var Lp: any
declare var Rp: any
declare var __: any
declare var Number: any
declare var Com: any
declare var Dot: any
declare var Sem: any
declare var Col: any
declare var Amp: any
declare var And: any
declare var Ca: any
declare var Quote: any
declare var Apo: any
declare var Slash: any
declare var Dash: any
declare var Punct: any
declare var Mc: any
declare var DutchPref: any
declare var Cap: any
declare var Lowword: any
declare var NL: any
declare var Misc: any
declare var End: any
declare var Et: any
declare var Low: any

import { lexer } from './lexer'

// TODO: [parser] It's currently extremely slow for large sentences, not good.
const getFullName = (name: {
  family: string
  'non-dropping-particle': string
}) =>
  `${
    name?.['non-dropping-particle'] ? name?.['non-dropping-particle'] + ' ' : ''
  }${name.family}`

const locators = [
  'act',
  'appendix',
  'article-locator',
  'book',
  'canon',
  'chapter',
  'column',
  'elocation',
  'equation',
  'figure',
  'folio',
  'issue',
  'line',
  'note',
  'opus',
  'page',
  'paragraph',
  'part',
  'rule',
  'scene',
  'section',
  'sub-verbo',
  'supplement',
  'table',
  'timestamp',
  'title-locator',
  'verse',
  'version',
  'volume',
]

const labelMap: { [key: string]: string } = {
  p: 'page',
  pp: 'page',
  chapter: 'chapter',
  ch: 'chapter',
  sec: 'section',
  par: 'paragraph',
  paras: 'paragraph',
  vol: 'volume',
  app: 'appendix',
}

interface NearleyToken {
  value: any
  [key: string]: any
}

interface NearleyLexer {
  reset: (chunk: string, info: any) => void
  next: () => NearleyToken | undefined
  save: () => any
  formatError: (token: never) => string
  has: (tokenType: string) => boolean
}

interface NearleyRule {
  name: string
  symbols: NearleySymbol[]
  postprocess?: (d: any[], loc?: number, reject?: {}) => any
}

type NearleySymbol =
  | string
  | { literal: any }
  | { test: (token: any) => boolean }

interface Grammar {
  Lexer: NearleyLexer | undefined
  ParserRules: NearleyRule[]
  ParserStart: string
}

const grammar: Grammar = {
  Lexer: lexer,
  ParserRules: [
    { name: 'Input$ebnf$1', symbols: ['InputContent'] },
    {
      name: 'Input$ebnf$1',
      symbols: ['Input$ebnf$1', 'InputContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'Input',
      symbols: ['Input$ebnf$1'],
      postprocess: (inp: any[]) => {
        const [content] = inp
        return content.reduce((acc: any[], curr: Record<string, any>) => {
          if (!curr.value) {
            acc.push(curr)
            return acc
          }

          if (typeof acc[acc.length - 1] === 'string') {
            acc[acc.length - 1] += curr.value
            return acc
          }

          acc.push(curr.value)
          return acc
        }, [])
      },
    },
    { name: 'InputContent', symbols: ['ParenCite'], postprocess: id },
    { name: 'InputContent', symbols: ['NarrCite'], postprocess: id },
    { name: 'InputContent', symbols: ['NonCiteContent'], postprocess: id },
    {
      name: 'NonCiteContent',
      symbols: [lexer.has('Year') ? { type: 'Year' } : Year],
      postprocess: id,
    },
    {
      name: 'NonCiteContent',
      symbols: ['NonYearParenContent'],
      postprocess: id,
    },
    { name: 'NonCiteContent$ebnf$1', symbols: ['NonYearParenContent'] },
    {
      name: 'NonCiteContent$ebnf$1',
      symbols: ['NonCiteContent$ebnf$1', 'NonYearParenContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'NonCiteContent',
      symbols: [
        lexer.has('Lp') ? { type: 'Lp' } : Lp,
        'NonCiteContent$ebnf$1',
        lexer.has('Rp') ? { type: 'Rp' } : Rp,
      ],
      postprocess: ([l, c, r]) => l + c.join('') + r,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('__') ? { type: '__' } : __],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Number') ? { type: 'Number' } : Number],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Com') ? { type: 'Com' } : Com],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Dot') ? { type: 'Dot' } : Dot],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Sem') ? { type: 'Sem' } : Sem],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Col') ? { type: 'Col' } : Col],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Amp') ? { type: 'Amp' } : Amp],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('And') ? { type: 'And' } : And],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Ca') ? { type: 'Ca' } : Ca],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Quote') ? { type: 'Quote' } : Quote],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Apo') ? { type: 'Apo' } : Apo],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Slash') ? { type: 'Slash' } : Slash],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Dash') ? { type: 'Dash' } : Dash],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Punct') ? { type: 'Punct' } : Punct],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Mc') ? { type: 'Mc' } : Mc],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('DutchPref') ? { type: 'DutchPref' } : DutchPref],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Cap') ? { type: 'Cap' } : Cap],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Lowword') ? { type: 'Lowword' } : Lowword],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('NL') ? { type: 'NL' } : NL],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('Misc') ? { type: 'Misc' } : Misc],
      postprocess: id,
    },
    {
      name: 'NonYearParenContent',
      symbols: [lexer.has('End') ? { type: 'End' } : End],
      postprocess: id,
    },
    { name: 'NarrCite$ebnf$1', symbols: ['Loc'], postprocess: id },
    { name: 'NarrCite$ebnf$1', symbols: [], postprocess: () => null },
    {
      name: 'NarrCite',
      symbols: [
        'NameList',
        lexer.has('__') ? { type: '__' } : __,
        lexer.has('Lp') ? { type: 'Lp' } : Lp,
        'YearList',
        'NarrCite$ebnf$1',
        lexer.has('Rp') ? { type: 'Rp' } : Rp,
      ],
      postprocess: ([name, , , yearlist]) => ({
        citationId: 'CITE-X',
        citationItems: yearlist.map((y: string[]) => ({
          id: getFullName(name[0]).replace(/ /g, '') + y[0],
          itemData: {
            author: name,
            issued: {
              'date-parts': [[y[0].replace(/(\d|.-?)[a-z]/, '$1')]],
            },
            ...(y[1]
              ? {
                  'original-date': {
                    'date-parts': [[y[1].replace(/(\d)[a-z]/, '$1')]],
                  },
                }
              : {}),
          },
        })),
        properties: { noteIndex: 0, mode: 'composite' },
      }),
    },
    {
      name: 'ParenCite',
      symbols: [
        lexer.has('Lp') ? { type: 'Lp' } : Lp,
        'ParenContent',
        lexer.has('Rp') ? { type: 'Rp' } : Rp,
      ],
      postprocess: ([, content]) => {
        // This is CSL-JSON cite items
        return {
          citationId: 'CITE-X',
          citationItems: content.flat(),
          properties: { noteIndex: 0 },
        }
      },
    },
    { name: 'ParenContent', symbols: ['SingleParenEntry'], postprocess: id },
    {
      name: 'ParenContent',
      symbols: [
        'ParenContent',
        lexer.has('Sem') ? { type: 'Sem' } : Sem,
        lexer.has('__') ? { type: '__' } : __,
        'SingleParenEntry',
      ],
      postprocess: ([content, semi, , single]) => [
        ...content.flat(),
        ...single,
      ],
    },
    {
      name: 'ParenContent',
      symbols: ['ParenContent', 'PreAuthsMiddle', 'SingleParenEntry'],
      postprocess: ([content, pre, single]) => {
        //const sing = single[0]
        if (pre) {
          single[0].prefix = pre.join('')
        }
        return [...content.flat(), ...single]
      },
    },
    { name: 'SingleParenEntry$ebnf$1', symbols: [] },
    {
      name: 'SingleParenEntry$ebnf$1',
      symbols: ['SingleParenEntry$ebnf$1', 'PreAuthsPre'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    { name: 'SingleParenEntry$ebnf$2', symbols: ['Loc'], postprocess: id },
    { name: 'SingleParenEntry$ebnf$2', symbols: [], postprocess: () => null },
    {
      name: 'SingleParenEntry',
      symbols: [
        'SingleParenEntry$ebnf$1',
        'ParenCiteAuthYear',
        'SingleParenEntry$ebnf$2',
      ],
      postprocess: ([pre, content, loc]) => {
        const l = Object.assign({}, loc)
        const p = pre.length ? { prefix: pre?.join('') } : {}

        if (content.length === 1) {
          content[0] = { ...content[0], ...l, ...p }
          return content
        }
        content[0] = { ...content[0], ...p }
        content[content.length - 1] = { ...content[content.length - 1], ...l }
        return content
      },
    },
    { name: 'PreAuthsPre$ebnf$1', symbols: ['GenericContent'] },
    {
      name: 'PreAuthsPre$ebnf$1',
      symbols: ['PreAuthsPre$ebnf$1', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'PreAuthsPre',
      symbols: [
        'PreAuthsPre$ebnf$1',
        lexer.has('Sem') ? { type: 'Sem' } : Sem,
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: (content) => {
        return content[0]
      },
    },
    { name: 'PreAuthsPre$ebnf$2', symbols: ['GenericContent'] },
    {
      name: 'PreAuthsPre$ebnf$2',
      symbols: ['PreAuthsPre$ebnf$2', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'PreAuthsPre',
      symbols: [
        'PreAuthsPre$ebnf$2',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: (content) => content[0],
    },
    { name: 'PreAuthsPre$ebnf$3', symbols: ['GenericContent'] },
    {
      name: 'PreAuthsPre$ebnf$3',
      symbols: ['PreAuthsPre$ebnf$3', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'PreAuthsPre',
      symbols: ['PreAuthsPre$ebnf$3', lexer.has('__') ? { type: '__' } : __],
      postprocess: (content) => content[0],
    },
    { name: 'PreAuthsMiddle$ebnf$1', symbols: ['GenericContent'] },
    {
      name: 'PreAuthsMiddle$ebnf$1',
      symbols: ['PreAuthsMiddle$ebnf$1', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'PreAuthsMiddle',
      symbols: [
        lexer.has('Sem') ? { type: 'Sem' } : Sem,
        lexer.has('__') ? { type: '__' } : __,
        'PreAuthsMiddle$ebnf$1',
      ],
      postprocess: (content) => {
        return content[2]
      },
    },
    {
      name: 'Loc',
      symbols: [
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'LocContent',
      ],
      postprocess: ([, , loc]) => loc,
    },
    { name: 'LocContent$ebnf$1', symbols: ['GenericContent'] },
    {
      name: 'LocContent$ebnf$1',
      symbols: ['LocContent$ebnf$1', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    { name: 'LocContent$ebnf$2', symbols: ['GenericContent'] },
    {
      name: 'LocContent$ebnf$2',
      symbols: ['LocContent$ebnf$2', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'LocContent',
      symbols: [
        'LocContent$ebnf$1',
        lexer.has('__') ? { type: '__' } : __,
        'LocContent$ebnf$2',
      ],
      postprocess: ([label, space, loc]) => {
        const rawLabel = label.join('').trim().toLowerCase().replace(/\./g, '')

        if (!labelMap[rawLabel] && !locators.includes(rawLabel)) {
          return {
            label: 'none',
            locator: label.join('') + space + loc.join(''),
          }
        }

        const properLabel = labelMap[rawLabel] || rawLabel

        return {
          label: properLabel,
          locator: loc.join('').trim(),
        }
      },
    },
    { name: 'LocContent$ebnf$3', symbols: ['GenericContent'] },
    {
      name: 'LocContent$ebnf$3',
      symbols: ['LocContent$ebnf$3', 'GenericContent'],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'LocContent',
      symbols: ['LocContent$ebnf$3'],
      postprocess: ([loc]) => ({ locator: loc.join(''), label: 'none' }),
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Lowword') ? { type: 'Lowword' } : Lowword],
      postprocess: id,
    },
    {
      name: 'GenericContent$ebnf$1',
      symbols: [lexer.has('Cap') ? { type: 'Cap' } : Cap],
    },
    {
      name: 'GenericContent$ebnf$1',
      symbols: [
        'GenericContent$ebnf$1',
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
      ],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'GenericContent',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        'GenericContent$ebnf$1',
      ],
      postprocess: ([cap, caps]) => cap + caps.join(''),
    },
    {
      name: 'GenericContent',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: (content) => content.join(''),
    },
    {
      name: 'GenericContent',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('Lowword') ? { type: 'Lowword' } : Lowword,
      ],
      postprocess: (content) => content.join(''),
    },
    {
      name: 'GenericContent$ebnf$2$subexpression$1',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('Dot') ? { type: 'Dot' } : Dot,
      ],
    },
    {
      name: 'GenericContent$ebnf$2',
      symbols: ['GenericContent$ebnf$2$subexpression$1'],
    },
    {
      name: 'GenericContent$ebnf$2$subexpression$2',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('Dot') ? { type: 'Dot' } : Dot,
      ],
    },
    {
      name: 'GenericContent$ebnf$2',
      symbols: [
        'GenericContent$ebnf$2',
        'GenericContent$ebnf$2$subexpression$2',
      ],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'GenericContent',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('Dot') ? { type: 'Dot' } : Dot,
        'GenericContent$ebnf$2',
      ],
      postprocess: ([c, d, content]) => c + d + content.flat().join(''),
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Col') ? { type: 'Col' } : Col],
      postprocess: id,
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Number') ? { type: 'Number' } : Number],
      postprocess: id,
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Dot') ? { type: 'Dot' } : Dot],
      postprocess: id,
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Dash') ? { type: 'Dash' } : Dash],
      postprocess: id,
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('Com') ? { type: 'Com' } : Com],
      postprocess: id,
    },
    {
      name: 'GenericContent',
      symbols: [lexer.has('__') ? { type: '__' } : __],
      postprocess: id,
    },
    {
      name: 'ParenCiteAuthYear',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'YearList',
      ],
      postprocess: (content) => {
        const [name, , , yearlist] = content
        return yearlist.map((y: string[]) => ({
          id: getFullName(name[0]).replace(/ /g, '') + y[0],
          itemData: {
            author: name,
            issued: {
              'date-parts': [[y[0].replace(/(\d)[a-z]/, '$1')]],
            },
            ...(y[1]
              ? {
                  'original-date': {
                    'date-parts': [[y[1].replace(/(\d)[a-z]/, '$1')]],
                  },
                }
              : {}),
          },
        }))
      },
    },
    { name: 'YearList', symbols: ['Year'], postprocess: (year) => year },
    {
      name: 'YearList$ebnf$1',
      symbols: [lexer.has('__') ? { type: '__' } : __],
    },
    {
      name: 'YearList$ebnf$1',
      symbols: ['YearList$ebnf$1', lexer.has('__') ? { type: '__' } : __],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'YearList',
      symbols: [
        'YearList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        'YearList$ebnf$1',
        'Year',
      ],
      postprocess: ([list, , , year]) => {
        return [...list, year]
      },
    },
    { name: 'NameList', symbols: ['Name'], postprocess: (name) => name },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Name',
      ],
      postprocess: ([name, , , n]) => [name, n].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, , , n]) => [name, n].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, , , , , n]) => [name, n].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: ([name, , , ,]) => [name].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, _, and, __, n]) => [name, n].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: ([name]) => [name].flat(),
    },
    {
      name: 'NameList',
      symbols: [
        'NameList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Etal',
      ],
      postprocess: ([name]) => [name].flat(),
    },
    {
      name: 'NameList',
      symbols: ['NameList', 'Etal'],
      postprocess: ([name]) => [name].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: ['ParenNameMaybe'],
      postprocess: (name) => name,
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Name',
      ],
      postprocess: ([name, , , n]) => [name, n].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, , , n]) => [name, n].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, , , , , n]) => [name, n].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('Com') ? { type: 'Com' } : Com,
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: ([name]) => [name].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
        'NameList',
      ],
      postprocess: ([name, , , , n]) => [name, n].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('__') ? { type: '__' } : __,
        'Comp',
        lexer.has('__') ? { type: '__' } : __,
      ],
      postprocess: ([name]) => [name].flat(),
    },
    {
      name: 'ParenNameMaybeList',
      symbols: [
        'ParenNameMaybeList',
        lexer.has('__') ? { type: '__' } : __,
        'Etal',
      ],
      postprocess: ([name]) => [name].flat(),
    },
    { name: 'ParenNameMaybe', symbols: ['Name'], postprocess: id },
    {
      name: 'ParenNameMaybe',
      symbols: [
        'ParenNameMaybe',
        lexer.has('__') ? { type: '__' } : __,
        'ParenNameMaybe',
      ],
      postprocess: ([n, , nn]) => ({
        ...n,
        ...nn,
        family: n.family + nn.family,
      }),
    },
    {
      name: 'ParenNameMaybe',
      symbols: [
        'ParenNameMaybe',
        lexer.has('__') ? { type: '__' } : __,
        lexer.has('Lowword') ? { type: 'Lowword' } : Lowword,
      ],
      postprocess: ([n, , nn]) => ({ ...n, family: n.family + nn }),
    },
    {
      name: 'Etal$ebnf$1',
      symbols: [lexer.has('__') ? { type: '__' } : __],
      postprocess: id,
    },
    { name: 'Etal$ebnf$1', symbols: [], postprocess: () => null },
    {
      name: 'Etal',
      symbols: ['Etal$ebnf$1', lexer.has('Et') ? { type: 'Et' } : Et],
      postprocess: (etal) => null,
    },
    {
      name: 'Name',
      symbols: ['Initials', lexer.has('__') ? { type: '__' } : __, 'LastName'],
      postprocess: ([initials, , name]) => ({
        given: initials.join(''),
        ...name,
      }),
    },
    { name: 'Name', symbols: ['LastName'], postprocess: id },
    { name: 'LastName', symbols: ['SingleName'], postprocess: id },
    { name: 'LastName', symbols: ['HyphenName'], postprocess: id },
    {
      name: 'Comp',
      symbols: [lexer.has('And') ? { type: 'And' } : And],
      postprocess: id,
    },
    {
      name: 'Comp',
      symbols: [lexer.has('Amp') ? { type: 'Amp' } : Amp],
      postprocess: id,
    },
    {
      name: 'HyphenName',
      symbols: [
        'SingleName',
        lexer.has('Dash') ? { type: 'Dash' } : Dash,
        'SingleName',
      ],
      postprocess: ([first, d, last]) => ({
        family: `${getFullName(first) + d + getFullName(last)}`,
      }),
    },
    {
      name: 'SingleName',
      symbols: ['BoringNameMaybe'],
      postprocess: ([name]) => ({ family: name }),
    },
    { name: 'SingleName', symbols: ['DutchName'], postprocess: id },
    { name: 'SingleName', symbols: ['OReilly'], postprocess: id },
    { name: 'SingleName', symbols: ['McConnel'], postprocess: id },
    { name: 'Initials', symbols: ['Initial'] },
    { name: 'Initials$ebnf$1', symbols: [] },
    {
      name: 'Initials$ebnf$1',
      symbols: ['Initials$ebnf$1', lexer.has('__') ? { type: '__' } : __],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    { name: 'Initials', symbols: ['Initials', 'Initials$ebnf$1', 'Initial'] },
    {
      name: 'Initial',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        lexer.has('Dot') ? { type: 'Dot' } : Dot,
      ],
      postprocess: id,
    },
    {
      name: 'DutchName',
      symbols: [
        'DutchPrefix',
        lexer.has('__') ? { type: '__' } : __,
        'BoringNameMaybe',
      ],
      postprocess: ([pref, space, rest]) => ({
        family: rest,
        'non-dropping-particle': pref.join(''),
      }),
    },
    {
      name: 'OReilly',
      symbols: ['BoringNameMaybe', { literal: "'" }, 'BoringNameMaybe'],
      postprocess: ([o, a, name]) => ({ family: o + a + name }),
    },
    {
      name: 'McConnel',
      symbols: [lexer.has('Mc') ? { type: 'Mc' } : Mc, 'BoringNameMaybe'],
      postprocess: (name) => ({ family: name.join('') }),
    },
    { name: 'BoringNameMaybe$ebnf$1', symbols: [] },
    {
      name: 'BoringNameMaybe$ebnf$1',
      symbols: [
        'BoringNameMaybe$ebnf$1',
        lexer.has('Lowword') ? { type: 'Lowword' } : Lowword,
      ],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'BoringNameMaybe',
      symbols: [
        lexer.has('Cap') ? { type: 'Cap' } : Cap,
        'BoringNameMaybe$ebnf$1',
      ],
      postprocess: ([cap, rest]) => `${cap}${rest.join('')}`,
    },
    {
      name: 'BoringWord$ebnf$1',
      symbols: [lexer.has('Low') ? { type: 'Low' } : Low],
    },
    {
      name: 'BoringWord$ebnf$1',
      symbols: ['BoringWord$ebnf$1', lexer.has('Low') ? { type: 'Low' } : Low],
      postprocess: (d) => d[0].concat([d[1]]),
    },
    {
      name: 'BoringWord',
      symbols: ['BoringWord$ebnf$1'],
      postprocess: (word) => word.join(''),
    },
    {
      name: 'DutchPrefix',
      symbols: [lexer.has('DutchPref') ? { type: 'DutchPref' } : DutchPref],
    },
    {
      name: 'DutchPrefix',
      symbols: [
        'DutchPrefix',
        lexer.has('__') ? { type: '__' } : __,
        lexer.has('DutchPref') ? { type: 'DutchPref' } : DutchPref,
      ],
    },
    {
      name: 'Year',
      symbols: [lexer.has('Year') ? { type: 'Year' } : Year],
      postprocess: ([year]) => [`${year}`.replace(/\./g, '').toUpperCase()],
    },
    {
      name: 'Year$ebnf$1',
      symbols: [lexer.has('Dash') ? { type: 'Dash' } : Dash],
      postprocess: id,
    },
    { name: 'Year$ebnf$1', symbols: [], postprocess: () => null },
    {
      name: 'Year',
      symbols: [
        lexer.has('Year') ? { type: 'Year' } : Year,
        'Year$ebnf$1',
        lexer.has('Lowword') ? { type: 'Lowword' } : Lowword,
      ],
      postprocess: ([year, , low]) => [year + low],
    },
    {
      name: 'Year',
      symbols: ['Year', lexer.has('Slash') ? { type: 'Slash' } : Slash, 'Year'],
      postprocess: (content) => {
        const [year, sl, year2] = content
        return [...year2, ...year]
      },
    },
  ],
  ParserStart: 'Input',
}

export default grammar

db736bdcae25cac0b8c989a779831ebc95853b69

Make this not error

Make this not error

https://github.com/JournalOfTrialAndError/JOTE/blob/26ce780dbe6a41031e1b9a4195d4f31c07a4d988/libs/jast-util-to-ltast/src/lib/types.ts#L73

  node: Node,
  type: string,
  props?: Properties,
  children?: string | Array<LtastContent>
) => LtastContent

export type JWithoutProps = (
  node: Node,
  type: string,
  children?: string | Array<LtastContent>
) => LtastContent

export type JWithPropsSpecific<TNode extends LtastContent = LtastContent> = (
  node: Node,
  type: Pick<TNode, 'type'>,
  props?: Properties,
  //@ts-expect-error yeah i know butttt
  // TODO: Make this not error
  children?: Pick<TNode, 'children'>
) => TNode

export type J = JWithProps & JWithoutProps & Context

79000c2a7c6c0ec23580137550649b8063f0e9f4

fix this type error

fix this type error

https://github.com/JournalOfTrialAndError/JOTE/blob/26ce780dbe6a41031e1b9a4195d4f31c07a4d988/libs/jast-util-to-ltast/src/lib/jast-util-to-ltast.ts#L38

    quotes: ['"'],
  }
) {
  // const byId: { [s: string]: Element } = {}
  let ltast: LtastContent | LtastRoot

  // TODO: fix this type error
  const j: J = Object.assign<JWithProps & JWithoutProps, Context>(
    (
      node: Node,
      type: string,
      props?: Properties | string | Array<LtastContent>,
      children?: string | Array<LtastContent>
    ) => {
      let properties: Properties | undefined

d057ed50b3f8ccdcc2633a2c172e50c54befcd8c

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.