Skip to content

Instantly share code, notes, and snippets.

@mellson
Last active May 13, 2021 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mellson/2de5f9adda94b928270131cec828c4b1 to your computer and use it in GitHub Desktop.
Save mellson/2de5f9adda94b928270131cec828c4b1 to your computer and use it in GitHub Desktop.
script to transpile ReScript Relay files to TypeScript components - call it with a src and destination. Example `node transpileRescriptRelay.js src/rescript src/rescript_dummy_ts_components`
var fs = require("fs")
if (process.argv.length < 4)
throw new Error("You need to provide a path to both source and destination")
const source = process.argv[2]
const destination = process.argv[3]
const fileType = process.argv[4] ?? "ts"
const relayFragmentIdentifiers = ["%relay", "fragment"]
const relayQueryIdentifiers = ["query"]
const isReScriptRelayModule = (string) =>
relayFragmentIdentifiers.every((identifier) => string.includes(identifier)) &&
!relayQueryIdentifiers.some((identifier) => string.includes(identifier))
const parseString = (input) => {
let result = []
if (!input) return result
input.split("module ").forEach((possibleModule) => {
if (isReScriptRelayModule(possibleModule)) {
const moduleName = possibleModule.substr(
0,
possibleModule.indexOf(" = %relay"),
)
const parameterString = `${moduleName}.use(`
const parameterIndex = input.indexOf(parameterString)
const parameterName = input.substr(
parameterIndex + parameterString.length,
input.substr(parameterIndex + parameterString.length).indexOf(")"),
)
const fragmentString = "fragment"
const fragmentIndex = possibleModule.indexOf(fragmentString)
const fragmentName = possibleModule.substr(
fragmentIndex + fragmentString.length + 1,
possibleModule.indexOf(" on") -
fragmentIndex -
fragmentString.length -
1,
)
const fragment = possibleModule.substr(
fragmentIndex,
possibleModule.indexOf("`)") - fragmentIndex,
)
if (
fragment.length > 0 &&
fragmentName.length > 0 &&
parameterName.length > 0
)
result.push({ fragment, fragmentName, parameterName })
}
})
return result
}
const buildComponent = (componentName, fragments) => {
let imports = []
let parameters = []
let useFragments = []
fragments.forEach(({ fragment, fragmentName, parameterName }) => {
imports.push(
`import { ${fragmentName}$key } from './__generated__/${fragmentName}.graphql'`,
)
parameters.push(`${parameterName}: ${fragmentName}$key`)
useFragments.push(`
useFragment(
graphql\`
${fragment}
\`,
${parameterName},
)`)
})
return `
import { graphql, useFragment } from 'react-relay'
${imports.join("\n")}
export const ${componentName} = (${parameters.join(",")}) => {
${useFragments.join("\n")}
}
`
}
const writeDummyComponent = (parent, file) => {
const filePath = `${parent}/${file}`
const fileContents = fs.readFileSync(filePath).toString()
if (isReScriptRelayModule(fileContents)) {
const componentName = file.split(".res")[0]
const componentFileName = `${componentName}.${fileType}`
const component = buildComponent(componentName, parseString(fileContents))
const componentDir = parent.replace(source, destination)
if (!fs.existsSync(componentDir))
fs.mkdirSync(componentDir, { recursive: true })
const componentPath = `${componentDir}/${componentFileName}`
console.log(`Transpiling: ${componentPath}`)
fs.writeFileSync(componentPath, component)
}
}
const isReScriptFile = (filePath) => filePath.endsWith(".res")
const isReScriptDir = (filePath) => {
return (
fs.lstatSync(filePath).isDirectory() && !filePath.includes("__generated__")
)
}
const convertFile = (parent, file) => {
const filePath = `${parent}/${file}`
if (isReScriptDir(filePath)) {
convertDir(filePath)
} else if (isReScriptFile(filePath)) {
writeDummyComponent(parent, file)
}
}
const convertDir = (path) => {
fs.readdir(path, (err, files) => {
files.forEach((file) => {
convertFile(path, file)
})
})
}
const cleanDestination = (path) => fs.rmdirSync(path, { recursive: true })
cleanDestination(destination)
convertDir(source)
fs.watch(source, { recursive: true }, (eventType, file) => {
if (fs.existsSync(`${source}/${file}`)) {
convertFile(source, file)
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment