Created
August 25, 2023 22:42
-
-
Save aschmoe/81e51ca4406a2ffef0019f7260d6d14b to your computer and use it in GitHub Desktop.
Script example for typescript-to-proptypes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
import path from 'path'; | |
import { fileURLToPath } from 'url'; | |
import glob from 'glob'; | |
import prettier from 'prettier'; | |
import rimraf from 'rimraf'; | |
import * as ttp from 'typescript-to-proptypes'; | |
/** | |
* Creates needed directories for a file path | |
* | |
* @param {string} filePath | |
*/ | |
function ensureDirectoryExistence(filePath) { | |
var dirname = path.dirname(filePath); | |
if (fs.existsSync(dirname)) { | |
return true; | |
} | |
ensureDirectoryExistence(dirname); | |
fs.mkdirSync(dirname); | |
} | |
/** | |
* Convert typescript -> proptypes | |
* | |
* Usage: convertTypesToProps('./theModule', '/src').catch((e) => console.error('failed', e)); | |
* | |
* @param {string} moduleDir Path to folder containing typescript config eg './theModule' | |
* @param {string} subDir Subpath to folder containing files for conversion eg '/src' | |
* @param {string} outputDir Path to folder to output converted files eg './propTypes' | |
*/ | |
export default async function convertTypesToProps( | |
moduleDir, | |
subDir = '', | |
outputDir = './propTypes' | |
) { | |
const __filename = fileURLToPath(import.meta.url); | |
const __dirname = path.dirname(__filename); | |
// Clear previous output | |
await new Promise((resolve) => | |
rimraf(path.resolve(__dirname, outputDir), () => resolve(true)) | |
); | |
const prettierConfig = await prettier.resolveConfig( | |
path.join(__dirname, '../.prettierrc') | |
); | |
const files = glob.sync(`${moduleDir}${subDir}/**/*.{d.ts,ts,tsx}`, { | |
ignore: ['**/*.{spec,stories}.{d.ts,ts,tsx}'], | |
absolute: true, | |
cwd: __dirname, | |
}); | |
// Create program for all files to speed up tests | |
const tsConfigPath = path.resolve(__dirname, `${moduleDir}/tsconfig.json`); | |
const program = ttp.createProgram(files, ttp.loadConfig(tsConfigPath)); | |
for (const file of files) { | |
const dirname = path.dirname(file); | |
const outputName = | |
dirname.substring(__dirname.length + 1) + | |
'/' + | |
file.substr(dirname.length + 1); | |
const astPath = path.resolve(__dirname, `${outputDir}/${outputName}.json`); | |
const outputPath = path.resolve(__dirname, `${outputDir}/${outputName}.js`); | |
const ast = ttp.parseFromProgram(file, program, { | |
checkDeclarations: true, | |
}); | |
//#region Check AST matches | |
// propsFilename will be different depending on where the project is on disk | |
// Manually check that it's correct and then delete it | |
const newAST = ttp.programNode( | |
ast.body.map((component) => { | |
return { ...component, propsFilename: undefined }; | |
}) | |
); | |
// Write AST to file | |
ensureDirectoryExistence(astPath); | |
const astJSON = await prettier.format( | |
JSON.stringify(newAST, (key, value) => { | |
// These are TypeScript internals that change depending on the number of symbols created during test | |
if (key === '$$id') { | |
return undefined; | |
} | |
return value; | |
}), | |
{ | |
...prettierConfig, | |
filepath: astPath, | |
} | |
); | |
fs.writeFileSync(astPath, astJSON); | |
// Transpile | |
let inputSource = ttp.ts.transpileModule(fs.readFileSync(file, 'utf8'), { | |
compilerOptions: { | |
target: ttp.ts.ScriptTarget.ESNext, | |
jsx: ttp.ts.JsxEmit.Preserve, | |
}, | |
}).outputText; | |
let result = ''; | |
// For d.ts files we just generate the AST | |
if (!inputSource) { | |
try { | |
result = ttp.generate(ast); | |
} catch (e) { | |
console.error(`Failed to generate ${file}, error: ${e.message}`); | |
continue; | |
} | |
} | |
// For .tsx? files we transpile them and inject the proptypes | |
else { | |
let injected; | |
try { | |
injected = ttp.inject(ast, inputSource, { | |
// removeExistingPropTypes: true, | |
babelOptions: { | |
filename: file, | |
}, | |
// From https://www.benmvp.com/blog/auto-generate-react-prop-types-typescript-components/ | |
comment: ` | |
=============== WARNING ================ | |
| These PropTypes are auto-generated | | |
| from the TypeScript type definitions | | |
======================================== | |
`, | |
}); | |
} catch (e) { | |
console.error(`Failed to inject ${file}, error: ${e.message}`); | |
continue; | |
} | |
result = injected; | |
} | |
const propTypes = await prettier.format(result, { | |
...prettierConfig, | |
filepath: outputPath, | |
}); | |
fs.writeFileSync(outputPath, propTypes); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment