mirror of https://github.com/vector-im/riot-web
Basic script to create react component with test and style files (#7757)
* basically working script Signed-off-by: Kerry Archibald <kerrya@element.io> * add test template * add skinned-sdk import to test temp * remove extra import Signed-off-by: Kerry Archibald <kerrya@element.io> * comments Signed-off-by: Kerry Archibald <kerrya@element.io>pull/21833/head
parent
91ccbe4395
commit
ebb1feee50
|
@ -38,6 +38,7 @@
|
||||||
"prunei18n": "matrix-prune-i18n",
|
"prunei18n": "matrix-prune-i18n",
|
||||||
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||||
"reskindex": "node scripts/reskindex.js -h header",
|
"reskindex": "node scripts/reskindex.js -h header",
|
||||||
|
"make-component": "node scripts/make-react-component.js",
|
||||||
"reskindex:watch": "node scripts/reskindex.js -h header -w",
|
"reskindex:watch": "node scripts/reskindex.js -h header -w",
|
||||||
"rethemendex": "res/css/rethemendex.sh",
|
"rethemendex": "res/css/rethemendex.sh",
|
||||||
"clean": "rimraf lib",
|
"clean": "rimraf lib",
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
const fs = require('fs/promises');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsophisticated script to create a styled, unit-tested react component.
|
||||||
|
* -filePath / -f : path to the component to be created, including new component name, excluding extension, relative to src
|
||||||
|
* -withStyle / -s : optional, flag to create a style file for the component. Defaults to false.
|
||||||
|
*
|
||||||
|
* eg:
|
||||||
|
* ```
|
||||||
|
* node srcipts/make-react-component.js -f components/toasts/NewToast -s
|
||||||
|
* ```
|
||||||
|
* creates files:
|
||||||
|
* - src/components/toasts/NewToast.tsx
|
||||||
|
* - test/components/toasts/NewToast-test.tsx
|
||||||
|
* - res/css/components/toasts/_NewToast.scss
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TEMPLATES = {
|
||||||
|
COMPONENT: `
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Props {}
|
||||||
|
|
||||||
|
const %%ComponentName%%: React.FC<Props> = () => {
|
||||||
|
return <div className='mx_%%ComponentName%%' />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default %%ComponentName%%;
|
||||||
|
`,
|
||||||
|
TEST: `
|
||||||
|
import React from 'react';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
|
||||||
|
import '%%SkinnedSdkPath%%';
|
||||||
|
import %%ComponentName%% from '%%RelativeComponentPath%%';
|
||||||
|
|
||||||
|
describe('<%%ComponentName%% />', () => {
|
||||||
|
const defaultProps = {};
|
||||||
|
const getComponent = (props = {}) =>
|
||||||
|
mount(<%%ComponentName%% {...defaultProps} {...props} />);
|
||||||
|
|
||||||
|
it('renders', () => {
|
||||||
|
const component = getComponent();
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
STYLE: `
|
||||||
|
.mx_%%ComponentName%% {
|
||||||
|
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
alias: {
|
||||||
|
filePath: 'f',
|
||||||
|
withStyle: 's'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = require('minimist')(process.argv, options);
|
||||||
|
|
||||||
|
const ensureDirectoryExists = async (filePath) => {
|
||||||
|
const dirName = path.parse(filePath).dir;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(dirName);
|
||||||
|
return;
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
|
await fs.mkdir(dirName, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeFile = async ({
|
||||||
|
filePath, componentName, extension, base, template, prefix, componentFilePath
|
||||||
|
}) => {
|
||||||
|
const newFilePath = path.join(base, path.dirname(filePath), `${prefix || ''}${path.basename(filePath)}${extension}`)
|
||||||
|
await ensureDirectoryExists(newFilePath);
|
||||||
|
|
||||||
|
const relativePathToComponent = path.parse(path.relative(path.dirname(newFilePath), componentFilePath || ''));
|
||||||
|
const importComponentPath = path.join(relativePathToComponent.dir, relativePathToComponent.name);
|
||||||
|
|
||||||
|
const skinnedSdkPath = path.relative(path.dirname(newFilePath), 'test/skinned-sdk')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.writeFile(newFilePath, fillTemplate(template, componentName, importComponentPath, skinnedSdkPath), { flag: 'wx' });
|
||||||
|
console.log(`Created ${path.relative(process.cwd(), newFilePath)}`);
|
||||||
|
return newFilePath;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'EEXIST') {
|
||||||
|
console.log(`File already exists ${path.relative(process.cwd(), newFilePath)}`);
|
||||||
|
return newFilePath;
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fillTemplate = (template, componentName, relativeComponentFilePath, skinnedSdkPath) =>
|
||||||
|
template.replace(/%%ComponentName%%/g, componentName)
|
||||||
|
.replace(/%%RelativeComponentPath%%/g, relativeComponentFilePath)
|
||||||
|
.replace(/%%SkinnedSdkPath%%/g, skinnedSdkPath)
|
||||||
|
|
||||||
|
|
||||||
|
const makeReactComponent = async () => {
|
||||||
|
const { filePath, withStyle } = args;
|
||||||
|
|
||||||
|
if (!filePath) {
|
||||||
|
throw new Error('No file path provided, did you forget -f?')
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentName = filePath.split('/').slice(-1).pop();
|
||||||
|
|
||||||
|
const componentFilePath = await makeFile({ filePath, componentName, base: 'src', extension: '.tsx', template: TEMPLATES.COMPONENT });
|
||||||
|
await makeFile({ filePath, componentFilePath, componentName, base: 'test', extension: '-test.tsx', template: TEMPLATES.TEST, componentName });
|
||||||
|
if (withStyle) {
|
||||||
|
await makeFile({ filePath, componentName, base: 'res/css', prefix: '_', extension: '.scss', template: TEMPLATES.STYLE });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper since await at the top level is not well supported yet
|
||||||
|
function run() {
|
||||||
|
(async function () {
|
||||||
|
await makeReactComponent();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
return;
|
||||||
|
|
Loading…
Reference in New Issue