From a3115fb491f871f27324e113535d0907e80a1f3c Mon Sep 17 00:00:00 2001 From: Sahar Avr Date: Tue, 21 Sep 2021 16:06:44 +0300 Subject: [PATCH] [FEAT] new component build - use template for component code [REFACTOR] clean code - implement popular eslint rules [CLEANUP] junk files - remove .DS_Store traces --- package.json | 2 +- src/.DS_Store | Bin 6148 -> 0 bytes src/extension.js | 2 +- src/utils/parse.js | 27 ++++---- src/utils/splitter.js | 151 +++++++++++++++++++++++------------------- 5 files changed, 97 insertions(+), 85 deletions(-) delete mode 100644 src/.DS_Store diff --git a/package.json b/package.json index 6609228..f77a4c5 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index fb0d5a0e664d744ea8cbb63e438e2c3b7d6de6b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOK;Oa5T0$*)@~$Zq#mfmVJi+?f|RsXka{v{$_a^wD~iM;*m%_#Io`;R<`6}) zxbpY`aN)q?e^5B|AM`iy0}w}MABdO~aY2ZpooM$P&(6%wewKH$06;oL*aC0>z``c5 zwv62gBF06VkvVM{m-~5do7C?+-t!S3q#b zl5$q#cld-#g^_r_*gyqd$)ic(rs}QX!6@5EY83 zLW#a&5EYJgs{LGzr9u@BM9z$P^vp!xP>7rz?Nqu0aTS{0Fkl#{GBB?m9dZ8e{QCZ1 z4Kh!L0mHz5#Q>>wy>17Wq|erw#fh_)Vta^9i0~^F$`I`IaV!;a6raN;1;?mR5M7O> TLbRZmKLU~l(-{W-Dg)mDi?g5x diff --git a/src/extension.js b/src/extension.js index 6021dd3..84aa60c 100644 --- a/src/extension.js +++ b/src/extension.js @@ -10,7 +10,7 @@ const { const activate = context => { context.subscriptions.push(vscode.commands.registerCommand( 'react-component-splitter.split', - async () => { + async () => { try { validateSelection(); diff --git a/src/utils/parse.js b/src/utils/parse.js index 2411dbd..ecf5ec0 100644 --- a/src/utils/parse.js +++ b/src/utils/parse.js @@ -65,7 +65,7 @@ const linter = new ( } } -); +)(); const transformCode = code => transformSync(code, { presets: [babelPresetReact], @@ -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, { @@ -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'); @@ -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); @@ -173,7 +173,6 @@ const eslintAutofix = (code, { filePath }) => { }; - module.exports = { eslintAutofix, getImports, @@ -183,4 +182,4 @@ module.exports = { getUsedImports, pretify, transformCode, -}; \ No newline at end of file +}; diff --git a/src/utils/splitter.js b/src/utils/splitter.js index f9bea39..f70f7f6 100644 --- a/src/utils/splitter.js +++ b/src/utils/splitter.js @@ -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}`); @@ -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())), @@ -89,7 +95,7 @@ const getFirstAndLastImportLineIndexes = codeLines => { return {firstImportLineIndex, lastImportLineIndex}; -} +}; const replaceSelection = async ({ reactElement, name }) => { @@ -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 => { @@ -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 => { @@ -143,7 +149,6 @@ const removeUnusedImports = async () => { }; - const updateOriginalComponent = async ({ newComponent }) => { await replaceSelection(newComponent); @@ -169,31 +174,13 @@ 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]}}`; } @@ -201,9 +188,30 @@ const buildPropsString = props => { }; +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 => { @@ -211,17 +219,22 @@ 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, @@ -242,4 +255,4 @@ module.exports = { askForComponentName, validateSelection, updateOriginalComponent, -}; \ No newline at end of file +};