Skip to content

Commit

Permalink
[FEAT] new component build - use template for component code
Browse files Browse the repository at this point in the history
[REFACTOR] clean code - implement popular eslint rules

[CLEANUP] junk files - remove .DS_Store traces
  • Loading branch information
Sahar Avr committed Sep 21, 2021
1 parent 31a8fb8 commit a3115fb
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 85 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "react-component-splitter",
"displayName": "React Component Splitter",
"description": "Splits long components into shorter, readable subcomponents",
"version": "0.51.0",
"version": "0.52.0",
"icon": "src/assets/icon.png",
"repository": {
"type": "git",
Expand Down
Binary file removed src/.DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion src/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const {
const activate = context => {
context.subscriptions.push(vscode.commands.registerCommand(
'react-component-splitter.split',
async () => {
async () => {
try {

validateSelection();
Expand Down
27 changes: 13 additions & 14 deletions src/utils/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const linter = new (

}
}
);
)();

const transformCode = code => transformSync(code, {
presets: [babelPresetReact],
Expand Down Expand Up @@ -102,7 +102,16 @@ const getUndefinedVars = code => {

};

const getUsedImports = (code, options = { transform: true }) => {
const getImports = (code, options = { transform: true }) => {

return _.chain(options?.transform ? transformCode(code) : code)
.split('\n')
.filter(codeLine => /^\s*import.*from.*/.test(codeLine))
.value();

};

const getUsedImports = (code, options = { transform: true }) => {

const {output} = linter.verifyAndFix(
options?.transform ? transformCode(code) : code, {
Expand All @@ -126,15 +135,6 @@ const pretify = code => {

};

const getImports = (code, options = { transform: true }) => {

return _.chain(options?.transform ? transformCode(code) : code)
.split('\n')
.filter(codeLine => /^\s*import.*from.*/.test(codeLine))
.value();

};

const getNumberOfLeadingSpaces = (code, options = { endToStart: false }) => {

const codeLines = _.split(code, '\n');
Expand All @@ -144,7 +144,7 @@ const getNumberOfLeadingSpaces = (code, options = { endToStart: false }) => {
}

const firstCodeLineIndex = _.findIndex(codeLines, line =>
options?.endToStart ? /^\s*[<|\/>].*$/.test(line) : /^\s*<.*$/.test(line));
(options?.endToStart ? /^\s*[<|\/>].*$/.test(line) : /^\s*<.*$/.test(line)));
const firstSpaceIndex = codeLines[firstCodeLineIndex].search(/\S/);

return Math.max(0, firstSpaceIndex);
Expand Down Expand Up @@ -173,7 +173,6 @@ const eslintAutofix = (code, { filePath }) => {

};


module.exports = {
eslintAutofix,
getImports,
Expand All @@ -183,4 +182,4 @@ module.exports = {
getUsedImports,
pretify,
transformCode,
};
};
151 changes: 82 additions & 69 deletions src/utils/splitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,62 @@ const fs = require('fs');
const path = require('path');
const parseUtils = require('./parse');

const replaceCodeByRange = (code, range, replaceValue) => {

const lines = _.split(code, '\n');

const { startIndex, endIndex } = _.reduce(lines, (res, line, index) => {

if (index < range.start.line) {
res.startIndex = (res.startIndex + _.size(line) + 1);
}

if (index === range.start.line) {
res.startIndex += range.start.character;
}

if (index < range.end.line) {
res.endIndex = (res.endIndex + _.size(line) + 1);
}

if (index === range.end.line) {
res.endIndex += range.end.character;
}

return res;

}, { startIndex: 0, endIndex: 0 });

return `${code.substring(0, startIndex)}${replaceValue}${code.substring(endIndex)}`;

};

const validateSelection = () => {

const editor = vscode.window.activeTextEditor;
const selection = editor.document.getText(editor.selection);

try { parseUtils.transformCode(`<>${selection}</>`); }
catch { throw new Error('Invalid selection. Make sure your selection represents a valid React component'); }
try {
parseUtils.transformCode(`<>${selection}</>`);
} catch {
throw new Error('Invalid selection. Make sure your selection represents a valid React component');
}

const codeWithoutSelection = replaceCodeByRange(editor.document.getText(), editor.selection, '<></>');

try { parseUtils.transformCode(codeWithoutSelection); }
catch { throw new Error('Invalid selection. Make sure the code remains valid without your selection'); }
try {
parseUtils.transformCode(codeWithoutSelection);
} catch {
throw new Error('Invalid selection. Make sure the code remains valid without your selection');
}

};

const buildComponentPath = name => {

const activeDocumentPath = vscode.window.activeTextEditor.document.uri.fsPath;
const activeDocumentExtension = activeDocumentPath.replace(/(.*)+\.[^\.]+/, '$1');
const nameWithoutExtension = name.replace(/\.[^\.]+$/, '');
const activeDocumentExtension = activeDocumentPath.replace(/(.*)+\.[^\\.]+/, '$1');
const nameWithoutExtension = name.replace(/\.[^\\.]+$/, '');

return path.join(activeDocumentPath, '..', `${nameWithoutExtension}.${activeDocumentExtension}`);

Expand All @@ -46,36 +82,6 @@ const askForComponentName = async () => {
return name;
};

const replaceCodeByRange = (code, range, replaceValue) => {

const lines = _.split(code, '\n');

const { startIndex, endIndex } = _.reduce(lines, (res, line, index) => {

if (index < range.start.line) {
res.startIndex = (res.startIndex + _.size(line) + 1);
}

if (index === range.start.line) {
res.startIndex = (res.startIndex + range.start.character);
}

if (index < range.end.line) {
res.endIndex = (res.endIndex + _.size(line) + 1);
}

if (index === range.end.line) {
res.endIndex = (res.endIndex + range.end.character);
}

return res;

}, { startIndex: 0, endIndex: 0 });

return `${code.substring(0, startIndex)}${replaceValue}${code.substring(endIndex)}`;

};

const getFullDocumentRange = document => new vscode.Range(
document.positionAt(0),
document.positionAt(_.size(document.getText())),
Expand All @@ -89,7 +95,7 @@ const getFirstAndLastImportLineIndexes = codeLines => {

return {firstImportLineIndex, lastImportLineIndex};

}
};

const replaceSelection = async ({ reactElement, name }) => {

Expand All @@ -101,7 +107,7 @@ const replaceSelection = async ({ reactElement, name }) => {
await editor.edit(edit => {
edit.replace(editor.selection, reactElement);
edit.insert(new vscode.Position((lastImportLineIndex + 1), 0), `import ${name} from './${name}';\n`);
});
});

parseUtils.eslintAutofix(document.getText(), { filePath: document.uri.fsPath })
.then(output => {
Expand All @@ -128,7 +134,7 @@ const removeUnusedImports = async () => {
const usedImportsString = _.join(parseUtils.getUsedImports(code), '\n');
const codeWithUsedImports = _.replace(code, importsString, usedImportsString);

await editor.edit(edit => edit.replace(getFullDocumentRange(document), codeWithUsedImports));
await editor.edit(edit => edit.replace(getFullDocumentRange(document), codeWithUsedImports));

parseUtils.eslintAutofix(document.getText(), { filePath: document.uri.fsPath })
.then(output => {
Expand All @@ -143,7 +149,6 @@ const removeUnusedImports = async () => {

};


const updateOriginalComponent = async ({ newComponent }) => {

await replaceSelection(newComponent);
Expand All @@ -169,59 +174,67 @@ const generateReactElement = ({ name, props, jsx }) => {
return `${leadingSpacesFromStart}<${name}${propsString}/>`;
};

const extractRelevantImportsAndProps = () => {

const editor = vscode.window.activeTextEditor;
const code = editor.document.getText();
const selection = editor.document.getText(editor.selection);

const selectionAndImports = `
${buildImportsString(parseUtils.getImports(code))}\n
export default () => (<>${selection}</>);
`;

return {
props: parseUtils.getUndefinedVars(selectionAndImports),
imports: parseUtils.getUsedImports(selectionAndImports),
};

};

const buildImportsString = imports => _.join(imports, '\n');

const buildPropsString = props => {

const numberOfProps = _.size(props);

if (numberOfProps > 2) { return `{\n ${_.join(props, `,\n `)},\n}`; }
if (numberOfProps > 2) { return `{\n ${_.join(props, ',\n ')},\n}`; }
if (numberOfProps === 2) { return `{${_.join(props, ', ')}}`; }
if (numberOfProps === 1) { return `{${props[0]}}`; }

return '';

};

const extractRelevantImportsAndProps = () => {

const editor = vscode.window.activeTextEditor;
const code = editor.document.getText();
const selection = editor.document.getText(editor.selection);

const selectionAndImports = `
${buildImportsString(parseUtils.getImports(code))}\n
export default () => (<>${selection}</>);
`;

return {
props: parseUtils.getUndefinedVars(selectionAndImports),
imports: parseUtils.getUsedImports(selectionAndImports),
};

};

const shouldWrapCodeWithEmptyTag = code => {
try { parseUtils.transformCode(code); }
catch { return true; }
try {
parseUtils.transformCode(code);
} catch {
return true;
}
};

const createNewComponent = async componentName => {

const editor = vscode.window.activeTextEditor;
const selection = editor.document.getText(editor.selection);
const { imports, props } = extractRelevantImportsAndProps(componentName);

const importsString = buildImportsString(imports);
const propsString = buildPropsString(props);
const jsx = (shouldWrapCodeWithEmptyTag(selection) ? `<>\n${selection}\n</>` : selection);

const newComponent = {
code: parseUtils.pretify(
`${buildImportsString(imports)}\n\n` +
code: parseUtils.pretify(_.template(
`<%= importsString %>
`const ${componentName} = (${buildPropsString(props)}) => (\n` +
`${shouldWrapCodeWithEmptyTag(selection) ? `<>\n${selection}\n</>` : selection}\n` +
`);\n\n` +
const <%= componentName %> = (<%= propsString %>) => (
<%= jsx %>
);
`export default ${componentName};\n`,
),
export default <%= componentName %>;
`,
)({ componentName, importsString, propsString, jsx })),
reactElement: generateReactElement({ name: componentName, props, jsx: selection }),
imports,
name: componentName,
Expand All @@ -242,4 +255,4 @@ module.exports = {
askForComponentName,
validateSelection,
updateOriginalComponent,
};
};

0 comments on commit a3115fb

Please sign in to comment.