diff --git a/change/@fluentui-codemods-2020-08-12-18-44-18-t-dama-sample_renameProp_from_config.json b/change/@fluentui-codemods-2020-08-12-18-44-18-t-dama-sample_renameProp_from_config.json new file mode 100644 index 00000000000000..550c45d612e8d0 --- /dev/null +++ b/change/@fluentui-codemods-2020-08-12-18-44-18-t-dama-sample_renameProp_from_config.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "add generic config mod", + "packageName": "@fluentui/codemods", + "email": "t-dama@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-08-12T22:44:18.748Z" +} diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 40a916766fa3bb..28a3eb1530d0da 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -19,7 +19,8 @@ "just": "just-scripts", "lint": "just-scripts lint", "start-test": "just-scripts jest-watch", - "test": "just-scripts test" + "test": "just-scripts test", + "update-snapshots": "just-scripts jest -u" }, "devDependencies": { "@fluentui/eslint-plugin": "^0.53.4", diff --git a/packages/codemods/src/codeMods/mods/configMod/configMod.ts b/packages/codemods/src/codeMods/mods/configMod/configMod.ts new file mode 100644 index 00000000000000..386cb4c2924b60 --- /dev/null +++ b/packages/codemods/src/codeMods/mods/configMod/configMod.ts @@ -0,0 +1,46 @@ +import { SourceFile } from 'ts-morph'; +import { CodeMod } from '../../types'; +import { findJsxTag, renameProp } from '../../utilities/index'; + +const jsonObj = require('../upgrades.json'); + +/* Intermediate file that reads upgrades.json and returns + a codemod object to be run. + TODO: Add error checks for 'undefined' */ +export function createCodeModFromJson(): CodeMod | undefined { + return { + run: (file: SourceFile) => { + try { + /* Codemod body, which can be added to */ + const funcs = getCodeModUtilitiesFromJson(file); + funcs.forEach(func => { + func(); + }); + } catch (e) { + return { success: false }; + } + return { success: true }; + }, + version: '100000', + name: jsonObj.name, + enabled: true, + }; +} + +/* Helper function that parses a json object for details about individual + codemods and formats each into a function. These functions are stored in + an array that is returned to the user. */ +export function getCodeModUtilitiesFromJson(file: SourceFile): (() => void)[] { + const functions = []; + const modDetails = jsonObj.upgrades; + for (let i = 0; i < modDetails.length; i++) { + if (modDetails[i].type === 'renameProp') { + const func = function() { + const tags = findJsxTag(file, modDetails[i].options.from.importName); + renameProp(tags, modDetails[i].options.from.toRename, modDetails[i].options.to.replacementName); + }; + functions.push(func); + } + } + return functions; +} diff --git a/packages/codemods/src/codeMods/mods/upgrades.json b/packages/codemods/src/codeMods/mods/upgrades.json new file mode 100644 index 00000000000000..f6ec001caea3fb --- /dev/null +++ b/packages/codemods/src/codeMods/mods/upgrades.json @@ -0,0 +1,33 @@ +{ + "name": "@fluentui.react", + "upgrades": [ + { + "name": "Renaming 'isDisabled' to 'disabled' in Dropdown", + "type": "renameProp", + "options": { + "from": { + "importName": "Dropdown", + "paths": ["office-ui-fabric-react/lib"], + "toRename": "isDisabled" + }, + "to": { + "replacementName": "disabled" + } + } + }, + { + "name": "Renaming 'toggled' to 'checked' in CompoundButton WITH spread", + "type": "renameProp", + "options": { + "from": { + "importName": "CompoundButton", + "paths": ["office-ui-fabric-react/lib"], + "toRename": "toggled" + }, + "to": { + "replacementName": "checked" + } + } + } + ] +} diff --git a/packages/codemods/src/codeMods/tests/configMod/__snapshots__/configMod.test.ts.snap b/packages/codemods/src/codeMods/tests/configMod/__snapshots__/configMod.test.ts.snap new file mode 100644 index 00000000000000..faa41fd178988b --- /dev/null +++ b/packages/codemods/src/codeMods/tests/configMod/__snapshots__/configMod.test.ts.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tests a simple data-driven codeMod can rename props in CompoundButton and Dropdown 1`] = ` +"import * as React from 'react'; +import { CompoundButton } from 'office-ui-fabric-react/lib/Button'; + +export class RenderButton extends React.Component { + public render(): JSX.Element { + const { toggled, id, ...restProps } = this.props; + return ( +
+ + Button! + +
+ ); + } +} + +export interface LocalButtonProps { + id: string; + description?: string; + imageSource: string; + toggled?: boolean; +} +" +`; + +exports[`Tests a simple data-driven codeMod can rename props in CompoundButton and Dropdown 2`] = ` +"import * as React from 'react'; +import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const RenderDropdown = (props: any) => { + return ( +
+ + + {' '} + Woo Hoo!{' '} + +
+ ); +}; +" +`; diff --git a/packages/codemods/src/codeMods/tests/configMod/configMod.test.ts b/packages/codemods/src/codeMods/tests/configMod/configMod.test.ts new file mode 100644 index 00000000000000..8fd6201958a304 --- /dev/null +++ b/packages/codemods/src/codeMods/tests/configMod/configMod.test.ts @@ -0,0 +1,37 @@ +import { Project } from 'ts-morph'; +import { createCodeModFromJson } from '../../mods/configMod/configMod'; +import { Maybe } from '../../../helpers/maybe'; +import { runMods } from '../../../modRunner/runnerUtilities'; + +const buttonPath = '/**/tests/mock/**/button/**/*.tsx'; +const dropDownPath = '/**/tests/mock/**/dropdown/**/*.tsx'; + +describe('Tests a simple data-driven codeMod', () => { + let project: Project; + + beforeEach(() => { + project = new Project(); + project.addSourceFilesAtPaths(`${process.cwd()}${buttonPath}`); + project.addSourceFilesAtPaths(`${process.cwd()}${dropDownPath}`); + }); + + // TODO: Can you raise more errors in renameProp for better mod logging? + // TODO: Add value change examples && more mods in a later, larger PR. + + it('can rename props in CompoundButton and Dropdown', () => { + const mod = Maybe(createCodeModFromJson()); + if (mod.something) { + const mods = []; + mods.push(mod.value); + runMods(mods, project.getSourceFiles(), result => { + if (result.error) { + console.error(`Error running mod ${result.mod.name} on file ${result.file.getBaseName()}`, result.error); + } else { + console.log(`Upgraded file ${result.file.getBaseName()} with mod ${result.mod.name}`); + } + }); + } + expect(project.getSourceFile('mCompoundButtonProps.tsx')?.getFullText()).toMatchSnapshot(); + expect(project.getSourceFile('mDropdownProps.tsx')?.getFullText()).toMatchSnapshot(); + }); +}); diff --git a/packages/codemods/src/codeMods/tests/oldToNewButton/oldToNewButton.test.ts b/packages/codemods/src/codeMods/tests/oldToNewButton/oldToNewButton.test.ts index 1517e49f043999..4d16534ea524b0 100644 --- a/packages/codemods/src/codeMods/tests/oldToNewButton/oldToNewButton.test.ts +++ b/packages/codemods/src/codeMods/tests/oldToNewButton/oldToNewButton.test.ts @@ -12,15 +12,6 @@ describe('Persona props mod tests', () => { project.addSourceFilesAtPaths(`${process.cwd()}${dropDownPath}`); }); - it('can rename the toggled feature in Button', () => { - const file = project.getSourceFileOrThrow('mButtonProps.tsx'); - const tags = findJsxTag(file, 'Button'); - renameProp(tags, 'toggled', 'checked'); - tags.forEach(val => { - expect(val.getAttribute('toggled')).not.toBeTruthy(); - }); - }); - it('can work on the dropdown example', () => { const file = project.getSourceFileOrThrow('mDropdownSpreadProps.tsx'); const tags = findJsxTag(file, 'Dropdown');