diff --git a/README.md b/README.md
index 4ca1b5c..3044295 100644
--- a/README.md
+++ b/README.md
@@ -9,4 +9,9 @@ git clone git@github.com:ant-design/codemod.git antd-codemod
npx jscodeshift -t antd-codemod/transforms/v3-Icon-to-v4-Icon.js src/**/*.js --parser=babylon
npx jscodeshift -t antd-codemod/transforms/v3-Icon-to-v4-Form.js src/**/*.js --parser=babylon
npx jscodeshift -t antd-codemod/transforms/v3-LocaleProvider-to-v4-ConfigProvider.js src/**/*.js --parser=babylon
+npx jscodeshift -t antd-codemod/transforms/v4-Icon-Outlined.js src/**/*.js --parser=babylon
```
+
+**tips**
+
+If you are using typescript, you can use `--parser=tsx` option.
diff --git a/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.input.js b/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.input.js
new file mode 100644
index 0000000..2b7d90a
--- /dev/null
+++ b/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.input.js
@@ -0,0 +1,13 @@
+import { Smile as SmileO, SmileFilled, SmileTwoTone } from '@ant-design/icons';
+
+const Component = () => {
+ return ;
+};
+
+const Component1 = props => {
+ return ;
+};
+
+const Component2 = props => {
+ return ;
+};
diff --git a/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.output.js b/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.output.js
new file mode 100644
index 0000000..df22182
--- /dev/null
+++ b/transforms/__testfixtures__/v4-Icon-Outlined/alias-import.output.js
@@ -0,0 +1,13 @@
+import { SmileFilled, SmileOutlined as SmileO, SmileTwoTone } from '@ant-design/icons';
+
+const Component = () => {
+ return ;
+};
+
+const Component1 = props => {
+ return ;
+};
+
+const Component2 = props => {
+ return ;
+};
diff --git a/transforms/__testfixtures__/v4-Icon-Outlined/basic.input.js b/transforms/__testfixtures__/v4-Icon-Outlined/basic.input.js
new file mode 100644
index 0000000..7fd9bd6
--- /dev/null
+++ b/transforms/__testfixtures__/v4-Icon-Outlined/basic.input.js
@@ -0,0 +1,13 @@
+import { Smile, SmileFilled, SmileTwoTone } from '@ant-design/icons';
+
+const Component = () => {
+ return ;
+};
+
+const Component1 = props => {
+ return ;
+};
+
+const Component2 = props => {
+ return ;
+};
diff --git a/transforms/__testfixtures__/v4-Icon-Outlined/basic.output.js b/transforms/__testfixtures__/v4-Icon-Outlined/basic.output.js
new file mode 100644
index 0000000..ffb8dea
--- /dev/null
+++ b/transforms/__testfixtures__/v4-Icon-Outlined/basic.output.js
@@ -0,0 +1,13 @@
+import { SmileFilled, SmileOutlined, SmileTwoTone } from '@ant-design/icons';
+
+const Component = () => {
+ return ;
+};
+
+const Component1 = props => {
+ return ;
+};
+
+const Component2 = props => {
+ return ;
+};
diff --git a/transforms/__tests__/v4-Icon-Outlined.test.js b/transforms/__tests__/v4-Icon-Outlined.test.js
new file mode 100644
index 0000000..035437c
--- /dev/null
+++ b/transforms/__tests__/v4-Icon-Outlined.test.js
@@ -0,0 +1,11 @@
+const tests = ['basic', 'alias-import'];
+
+const defineTest = require('jscodeshift/dist/testUtils').defineTest;
+
+const testUnit = 'v4-Icon-Outlined';
+
+describe(testUnit, () => {
+ tests.forEach(test =>
+ defineTest(__dirname, testUnit, null, `${testUnit}/${test}`),
+ );
+});
diff --git a/transforms/v4-Icon-Outlined.js b/transforms/v4-Icon-Outlined.js
new file mode 100644
index 0000000..a8b60df
--- /dev/null
+++ b/transforms/v4-Icon-Outlined.js
@@ -0,0 +1,80 @@
+const allIcons = require('@ant-design/icons/lib/icons');
+
+const { printOptions } = require('./utils/config');
+const { removeEmptyModuleImport, addSubmoduleImport } = require('./utils');
+
+const outlinedIcons = Object.keys(allIcons)
+ .filter(n => n.endsWith('Outlined'))
+ .map(n => n.replace(/Outlined$/, ''));
+
+module.exports = (file, api, options) => {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ // remove old LocaleProvider imports
+ function renameV4IconWithoutOutlinedImport(j, root) {
+ let hasChanged = false;
+
+ // import { LocaleProvider } from 'antd';
+ root
+ .find(j.Identifier)
+ .filter(
+ path =>
+ outlinedIcons.includes(path.node.name) &&
+ path.parent.node.type === 'ImportSpecifier' &&
+ path.parent.parent.node.source.value === '@ant-design/icons',
+ )
+ .forEach(path => {
+ hasChanged = true;
+ const localComponentName = path.parent.node.local.name;
+ const importComponentName = path.parent.node.imported.name;
+
+ const importDeclaration = path.parent.parent.node;
+ importDeclaration.specifiers = importDeclaration.specifiers.filter(
+ specifier =>
+ !specifier.imported ||
+ specifier.imported.name !== importComponentName,
+ );
+
+ const outlinedIconName = importComponentName + 'Outlined';
+
+ if (localComponentName === importComponentName) {
+ addSubmoduleImport(j, root, '@ant-design/icons', outlinedIconName);
+ if (localComponentName === importComponentName) {
+ root
+ .findJSXElements(localComponentName)
+ .find(j.JSXIdentifier, {
+ name: localComponentName,
+ })
+ .forEach(nodePath => {
+ nodePath.node.name = outlinedIconName;
+ });
+ }
+ } else {
+ addSubmoduleImport(
+ j,
+ root,
+ '@ant-design/icons',
+ outlinedIconName,
+ localComponentName,
+ );
+ }
+ });
+
+ return hasChanged;
+ }
+
+ // step1. remove LocaleProvider import from antd
+ // step2. add ConfigProvider import from antd
+ // step3. cleanup antd import if empty
+ let hasChanged = false;
+ hasChanged = renameV4IconWithoutOutlinedImport(j, root) || hasChanged;
+
+ if (hasChanged) {
+ removeEmptyModuleImport(j, root, 'antd');
+ }
+
+ return hasChanged
+ ? root.toSource(options.printOptions || printOptions)
+ : null;
+};