Skip to content

Instantly share code, notes, and snippets.

@aschmoe
Created August 25, 2023 22:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aschmoe/81e51ca4406a2ffef0019f7260d6d14b to your computer and use it in GitHub Desktop.
Save aschmoe/81e51ca4406a2ffef0019f7260d6d14b to your computer and use it in GitHub Desktop.
Script example for typescript-to-proptypes
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