Skip to content

Commit

Permalink
Codemods: Implement Result and prepare for logging update. (#14548)
Browse files Browse the repository at this point in the history
* result implemented

* add logger interface

* tweak no op result

* Change files
  • Loading branch information
joschect authored Aug 17, 2020
1 parent f550420 commit 160f614
Show file tree
Hide file tree
Showing 14 changed files with 67 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Codemods: Implement result and prepare for logging changes",
"packageName": "@fluentui/codemods",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-08-15T19:38:06.133Z"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { runComponentToCompat, buildCompatHash, RawCompat, ComponentToCompat, getNamedExports } from './compatHelpers';
import { CodeMod } from '../../types';
import { SourceFile } from 'ts-morph';
import { Ok } from '../../../helpers/result';

// Not sure if this the best way to get all the things exported from button. It's dependent on version
// And other things. Ideally we'd be able to get it from within ts-morph.
Expand Down Expand Up @@ -40,7 +41,7 @@ export function createComponentToCompat(comp: RawCompat): ComponentToCompat {
const ComponentToCompat: CodeMod = {
run: (file: SourceFile) => {
runComponentToCompat(file, buildCompatHash(exportMapping, createComponentToCompat), fabricindex);
return { success: true };
return Ok({ logs: ['Moved imports to compat'] });
},
name: 'ComponentToCompat',
version: '1.0.0',
Expand Down
5 changes: 3 additions & 2 deletions packages/codemods/src/codeMods/mods/configMod/configMod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CodeModMapType,
} from '../../types';
import { findJsxTag, renameProp, getImportsByPath, repathImport } from '../../utilities/index';
import { Ok, Err } from '../../../helpers/result';

const jsonObj: UpgradeJSONType = require('../upgrades.json');

Expand All @@ -23,9 +24,9 @@ export function createCodeModFromJson(): CodeMod | undefined {
func();
});
} catch (e) {
return { success: false };
Err({ reason: 'Error' });
}
return { success: true };
return Ok({ logs: ['Updated Successfully'] });
},
version: '100000',
name: jsonObj.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SourceFile } from 'ts-morph';
import { CodeMod } from '../../types';
import { getImportsByPath, repathImport } from '../../utilities/index';
import { Ok } from '../../../helpers/result';

const searchString = /^office\-ui\-fabric\-react/;
const newString = '@fluentui/react';
Expand All @@ -11,7 +12,7 @@ const RepathOfficeToFluentImports: CodeMod = {
imports.forEach(val => {
repathImport(val, newString, searchString);
});
return { success: true };
return Ok({ logs: ['Replaced office-ui-fabric-react imports with @fluentui'] });
},
name: 'RepathOfficeImportsToFluent',
version: '1.0.0',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { SourceFile } from 'ts-morph';
import { CodeMod } from '../../types';
import { renameProp, findJsxTag } from '../../utilities/index';
import { Ok, Err } from '../../../helpers/result';

const oldToNewButton: CodeMod = {
run: (file: SourceFile) => {
try {
const tags = findJsxTag(file, 'DefaultButton');
renameProp(tags, 'toggled', 'checked');
} catch (e) {
return { success: false };
return Err({ reason: 'Error' });
}
return { success: true };
return Ok({ logs: ['Upgrade completed'] });
},
version: '100000',
name: 'oldToNewButton',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from 'ts-morph';
import { findJsxTag, appendOrCreateNamedImport } from '../../utilities/index';
import { CodeMod } from '../../types';
import { Ok, Err } from '../../../helpers/result';

const personaPath = 'office-ui-fabric-react/lib/Persona';

Expand Down Expand Up @@ -169,9 +170,9 @@ const PersonaToAvatarMod: CodeMod = {
renamePrimaryTextProp(file);
renameRenderCoin(file);
} catch (e) {
return { success: false };
return Err({ reason: 'Error', log: JSON.stringify(e) });
}
return { success: true };
return Ok({ logs: ['Replaced Persona with Avatar'] });
},
version: '100000',
name: 'PersonaToAvatar',
Expand Down
11 changes: 8 additions & 3 deletions packages/codemods/src/codeMods/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { SourceFile, JsxExpression, JsxOpeningElement, JsxSelfClosingElement } from 'ts-morph';
import { Result } from '../helpers/result';

export interface CodeModResult {
success?: boolean;
export interface ModResult {
logs: string[];
}

export type NoOp = {
reason: string;
log?: string;
};
export type CodeModResult = Result<ModResult, NoOp>;
export interface CodeMod<T = SourceFile> {
/**
* Each type of codemod can have multiple versions which work on different versions of its targeted package.
Expand Down
10 changes: 10 additions & 0 deletions packages/codemods/src/modRunner/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface LogFunction {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(...args: any[]): void;
}

export interface Logger {
log: LogFunction;
warn: LogFunction;
error: LogFunction;
}
7 changes: 4 additions & 3 deletions packages/codemods/src/modRunner/runnerUtilities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CodeMod } from '../codeMods/types';
import { Glob } from 'glob';
import { Maybe, Nothing, Something } from '../helpers/maybe';
import { Logger } from './logger';

// TODO ensure that async for all these utilities works
export function runMods<T>(
Expand Down Expand Up @@ -71,12 +72,12 @@ export function loadMod(path: string, errorCallback: (e: Error) => void): Maybe<
return Nothing();
}

export function getEnabledMods(getPaths = getModsPaths, loadM = loadMod) {
export function getEnabledMods(logger: Logger, getPaths = getModsPaths, loadM = loadMod) {
return getPaths()
.map(pth => {
console.log('fetching codeMod at ', pth);
logger.log('fetching codeMod at ', pth);
return loadM(pth, e => {
console.error(e);
logger.error(e);
});
})
.filter(modEnabled)
Expand Down
5 changes: 3 additions & 2 deletions packages/codemods/src/modRunner/tests/command.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CommandParser, CommandParserResult, yargsParse } from '../../command';
import { Ok } from '../../helpers/result';

describe('command parser', () => {
describe('when called with a single argument', () => {
Expand Down Expand Up @@ -39,7 +40,7 @@ describe('command parser', () => {
result.modsFilter({
name: 'one',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
Expand All @@ -50,7 +51,7 @@ describe('command parser', () => {
result.modsFilter({
name: 'one',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(false);
Expand Down
13 changes: 7 additions & 6 deletions packages/codemods/src/modRunner/tests/filters.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getModFilter, getRegexFilter, getStringFilter } from '../modFilter';
import { Maybe } from '../../helpers/maybe';
import { Ok } from '../../helpers/result';

describe('modRunner tests', () => {
it('gets a basic exact name match filter from string', () => {
Expand All @@ -21,23 +22,23 @@ describe('modRunner tests', () => {
modFilter({
name: 'ohi',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
expect(
modFilter({
name: 'bar',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
expect(
modFilter({
name: 'foo',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
Expand All @@ -49,23 +50,23 @@ describe('modRunner tests', () => {
modFilter({
name: 'hi',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
expect(
modFilter({
name: 'o zz o',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(true);
expect(
modFilter({
name: 'I wont be filtered!',
run: () => {
return {};
return Ok({ logs: [] });
},
}),
).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CodeMod } from '../../../../codeMods/types';
import { Err } from '../../../../helpers/result';
const CodeMod: CodeMod<string> = {
run: () => {
return {};
return Err({ reason: 'No operation taken' });
},
version: '1.0.0',
name: 'CodeMod',
Expand Down
6 changes: 4 additions & 2 deletions packages/codemods/src/modRunner/tests/modRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '../runnerUtilities';
import { CodeMod, CodeModResult } from '../../codeMods/types';
import { Maybe, Nothing } from '../../helpers/maybe';
import { Ok } from '../../helpers/result';

describe('modRunner tests', () => {
it('gets the appropriate path to mods based on current dir', () => {
Expand Down Expand Up @@ -40,7 +41,7 @@ describe('modRunner tests', () => {
let runCount = 0;
const runCallBack = (foo: string): CodeModResult => {
runCount = runCount + 1;
return {};
return Ok({ logs: [] });
};
const mods: CodeMod<string>[] = [
{
Expand All @@ -65,7 +66,7 @@ describe('modRunner tests', () => {

it('filters enabled and nothing Mods', () => {
const runcallBack = (foo: string): CodeModResult => {
return {};
return Ok({ logs: [] });
};

// use a generator to simulate getting each mod back
Expand All @@ -89,6 +90,7 @@ describe('modRunner tests', () => {
const gen = modGen();

const filtered = getEnabledMods(
console,
() => ['1', '2', '3', '4'],
() => gen.next().value,
);
Expand Down
15 changes: 8 additions & 7 deletions packages/codemods/src/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { runMods, getTsConfigs, getEnabledMods } from './modRunner/runnerUtilities';
import { CommandParserResult } from './command';
import { Logger } from './modRunner/logger';
import { Project } from 'ts-morph';

// TODO actually do console logging, implement some nice callbacks.
// Injection point for logger so that it can easily be replaced.
const logger: Logger = console;
export function upgrade(options: CommandParserResult) {
const mods = getEnabledMods().filter(options.modsFilter);
const mods = getEnabledMods(logger).filter(options.modsFilter);

console.log('getting configs');
logger.log('getting configs');
const configs = getTsConfigs();

configs.forEach(configString => {
Expand All @@ -17,14 +18,14 @@ export function upgrade(options: CommandParserResult) {
const files = project.getSourceFiles();
runMods(mods, files, result => {
if (result.error) {
console.error(`Error running mod ${result.mod.name} on file ${result.file.getBaseName()}`, result.error);
logger.error(`Error running mod ${result.mod.name} on file ${result.file.getBaseName()}`, result.error);
error = true;
} else {
console.log(`Upgraded file ${result.file.getBaseName()} with mod ${result.mod.name}`);
logger.log(`Upgraded file ${result.file.getBaseName()} with mod ${result.mod.name}`);
}
});
} catch (e) {
console.error(e);
logger.error(e);
error = true;
}
if (!error) {
Expand Down

0 comments on commit 160f614

Please sign in to comment.