<commit_before>import * as R from 'ramda'
import * as React from 'react'

import * as quiltConfigs from 'constants/quiltConfigs'
import { detect as isMarkdown } from 'components/Preview/loaders/Markdown'
import * as PreviewUtils from 'components/Preview/loaders/utils'
import * as AWS from 'utils/AWS'
import type { S3HandleBase } from 'utils/s3paths'
import type * as Model from 'model'

import { Mode, EditorInputType } from './types'

const cache: { [index in Mode]?: Promise<void> | 'fullfilled' } = {}
export const loadMode = (mode: Mode) => {
  if (cache[mode] === 'fullfilled') return cache[mode]
  if (cache[mode]) throw cache[mode]

  cache[mode] = import(`brace/mode/${mode}`).then(() => {
    cache[mode] = 'fullfilled'
  })
  throw cache[mode]
}

const isQuiltConfig = (path: string) =>
  quiltConfigs.all.some((quiltConfig) => quiltConfig.includes(path))
const typeQuiltConfig: EditorInputType = {
  brace: '__quiltConfig',
}

const isCsv = PreviewUtils.extIn(['.csv', '.tsv', '.tab'])
const typeCsv: EditorInputType = {
  brace: 'less',
}

const isJson = PreviewUtils.extIn(['.json'])
const typeJson: EditorInputType = {
  brace: 'json',
}

const typeMarkdown: EditorInputType = {
  brace: 'markdown',
}

const isText = PreviewUtils.extIn(['.txt', ''])
const typeText: EditorInputType = {
  brace: 'plain_text',
}

const isYaml = PreviewUtils.extIn(['.yaml', '.yml'])
const typeYaml: EditorInputType = {
  brace: 'yaml',
}

const typeNone: EditorInputType = {
  brace: null,
}

export const detect: (path: string) => EditorInputType = R.pipe(
  PreviewUtils.stripCompression,
  R.cond([
    [isQuiltConfig, R.always(typeQuiltConfig)],
    [isCsv, R.always(typeCsv)],
    [isJson, R.always(typeJson)],
    [isMarkdown, R.always(typeMarkdown)],
    [isText, R.always(typeText)],
    [isYaml, R.always(typeYaml)],
    [R.T, R.always(typeNone)],
  ]),
)

export const isSupportedFileType: (path: string) => boolean = R.pipe(
  detect,
  R.prop('brace'),
  Boolean,
)

export function useWriteData({
  bucket,
  key,
  version,
}: S3HandleBase): (value: string) => Promise<Model.S3File> {
  const s3 = AWS.S3.use()
  return React.useCallback(
    async (value) => {
      const { VersionId: latestVersion } = await s3
        .headObject({ Bucket: bucket, Key: key })
        .promise()
      if (latestVersion !== version) throw new Error('Revision is outdated')

      const { VersionId } = await s3
        .putObject({ Bucket: bucket, Key: key, Body: value })
        .promise()
      const { ContentLength: size } = await s3
        .headObject({ Bucket: bucket, Key: key, VersionId })
        .promise()
      return { bucket, key, size, version: VersionId }
    },
    [bucket, key, s3, version],
  )
}<commit_msg>Fix creating new files (#3132)<commit_after>import type { S3 } from 'aws-sdk'
import * as R from 'ramda'
import * as React from 'react'

import * as quiltConfigs from 'constants/quiltConfigs'
import { detect as isMarkdown } from 'components/Preview/loaders/Markdown'
import * as PreviewUtils from 'components/Preview/loaders/utils'
import * as AWS from 'utils/AWS'
import type { S3HandleBase } from 'utils/s3paths'
import type * as Model from 'model'

import { Mode, EditorInputType } from './types'

const cache: { [index in Mode]?: Promise<void> | 'fullfilled' } = {}
export const loadMode = (mode: Mode) => {
  if (cache[mode] === 'fullfilled') return cache[mode]
  if (cache[mode]) throw cache[mode]

  cache[mode] = import(`brace/mode/${mode}`).then(() => {
    cache[mode] = 'fullfilled'
  })
  throw cache[mode]
}

const isQuiltConfig = (path: string) =>
  quiltConfigs.all.some((quiltConfig) => quiltConfig.includes(path))
const typeQuiltConfig: EditorInputType = {
  brace: '__quiltConfig',
}

const isCsv = PreviewUtils.extIn(['.csv', '.tsv', '.tab'])
const typeCsv: EditorInputType = {
  brace: 'less',
}

const isJson = PreviewUtils.extIn(['.json'])
const typeJson: EditorInputType = {
  brace: 'json',
}

const typeMarkdown: EditorInputType = {
  brace: 'markdown',
}

const isText = PreviewUtils.extIn(['.txt', ''])
const typeText: EditorInputType = {
  brace: 'plain_text',
}

const isYaml = PreviewUtils.extIn(['.yaml', '.yml'])
const typeYaml: EditorInputType = {
  brace: 'yaml',
}

const typeNone: EditorInputType = {
  brace: null,
}

export const detect: (path: string) => EditorInputType = R.pipe(
  PreviewUtils.stripCompression,
  R.cond([
    [isQuiltConfig, R.always(typeQuiltConfig)],
    [isCsv, R.always(typeCsv)],
    [isJson, R.always(typeJson)],
    [isMarkdown, R.always(typeMarkdown)],
    [isText, R.always(typeText)],
    [isYaml, R.always(typeYaml)],
    [R.T, R.always(typeNone)],
  ]),
)

export const isSupportedFileType: (path: string) => boolean = R.pipe(
  detect,
  R.prop('brace'),
  Boolean,
)

type AWSError = Error & { code: string }

async function validToWriteFile(s3: S3, bucket: string, key: string, version?: string) {
  try {
    const { VersionId } = await s3.headObject({ Bucket: bucket, Key: key }).promise()
    return VersionId === version
  } catch (error) {
    if (error instanceof Error && (error as AWSError).code === 'NotFound') return true
    throw error
  }
}

export function useWriteData({
  bucket,
  key,
  version,
}: S3HandleBase): (value: string) => Promise<Model.S3File> {
  const s3 = AWS.S3.use()
  return React.useCallback(
    async (value) => {
      const valid = await validToWriteFile(s3, bucket, key, version)
      if (!valid) throw new Error('Revision is outdated')
      const { VersionId } = await s3
        .putObject({ Bucket: bucket, Key: key, Body: value })
        .promise()
      const { ContentLength: size } = await s3
        .headObject({ Bucket: bucket, Key: key, VersionId })
        .promise()
      return { bucket, key, size, version: VersionId }
    },
    [bucket, key, s3, version],
  )
}
{'fraction_non_alphanumeric': 0.1535953320748241, 'fraction_numerical': 0.004805217092843659, 'mean_word_length': 3.9222972972972974, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5206172', 'n_tokens_mistral': 1996, 'n_tokens_neox': 1957, 'n_words': 512}