@sanity/ui-codemod 1.0.0-alpha.2 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/_chunks-es/box.mods.js +4 -2
  2. package/dist/_chunks-es/box.mods.js.map +1 -1
  3. package/dist/_chunks-es/layout-mods.js +13 -1
  4. package/dist/_chunks-es/layout-mods.js.map +1 -1
  5. package/dist/_chunks-es/transformComponent.js +51 -22
  6. package/dist/_chunks-es/transformComponent.js.map +1 -1
  7. package/dist/transforms/latest/box/box.js +133 -18
  8. package/dist/transforms/latest/box/box.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/constants/latest/layout-mods.ts +12 -0
  11. package/src/transforms/latest/box/box.test.ts +202 -4
  12. package/src/transforms/latest/box/box.ts +36 -10
  13. package/src/utils/getElementMatchNames.ts +7 -0
  14. package/src/utils/getMappingValue.ts +18 -21
  15. package/src/utils/getNamedExportInit.ts +111 -0
  16. package/src/utils/getStaticAttributeExpression.ts +62 -9
  17. package/src/utils/getStyledComponentAliases.ts +85 -1
  18. package/src/utils/insertTodoWarning.ts +2 -14
  19. package/src/utils/parseModule.ts +29 -0
  20. package/src/utils/replaceElement.test.ts +28 -26
  21. package/src/utils/replaceElement.ts +4 -5
  22. package/src/utils/replaceStyledComponent.test.ts +40 -14
  23. package/src/utils/replaceStyledComponent.ts +2 -5
  24. package/src/utils/resolveRelativeModulePath.ts +37 -0
  25. package/src/utils/shouldTransformComponent.test.ts +14 -0
  26. package/src/utils/shouldTransformComponent.ts +5 -0
  27. package/src/utils/transformAttributes.test.ts +2 -2
  28. package/src/utils/transformStyledComponents.test.ts +80 -14
  29. package/src/utils/transformStyledComponents.ts +7 -16
  30. package/src/utils/getComponentJsxNames.ts +0 -17
@@ -1,8 +1,86 @@
1
1
  import "jscodeshift";
2
- import { insertTodoWarning, transformComponent, getComponentLocalNames, shouldTransformComponent, transformImport, transformAttributes, getStaticAttributeExpression } from "../../../_chunks-es/transformComponent.js";
2
+ import { getComponentLocalNames, insertTodoWarning, transformComponent, shouldTransformComponent, transformImport, transformAttributes, getStaticAttributeExpression } from "../../../_chunks-es/transformComponent.js";
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { resolve, dirname, join } from "node:path";
3
5
  import { replaceElement, BOX_MODS } from "../../../_chunks-es/box.mods.js";
4
6
  import { FLEX_MODS } from "../../../_chunks-es/flex.mods.js";
5
7
  import { GRID_MODS } from "../../../_chunks-es/grid.mods.js";
8
+ const parseCache = /* @__PURE__ */ new Map();
9
+ function parseModule(j, filePath) {
10
+ const cached = parseCache.get(filePath);
11
+ if (cached)
12
+ return cached;
13
+ try {
14
+ const source = readFileSync(filePath, "utf8"), root = j(source);
15
+ return parseCache.set(filePath, root), root;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+ const EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
21
+ function resolveRelativeModulePath(fromFilePath, moduleSpecifier) {
22
+ if (!moduleSpecifier.startsWith("."))
23
+ return null;
24
+ const base = resolve(dirname(fromFilePath), moduleSpecifier);
25
+ for (const ext of EXTENSIONS) {
26
+ const filePath = `${base}${ext}`;
27
+ if (existsSync(filePath))
28
+ return filePath;
29
+ }
30
+ for (const ext of EXTENSIONS) {
31
+ const indexPath = join(base, `index${ext}`);
32
+ if (existsSync(indexPath))
33
+ return indexPath;
34
+ }
35
+ return existsSync(base) ? base : null;
36
+ }
37
+ function findVariableInit(j, root, name) {
38
+ let init = null;
39
+ return root.find(j.VariableDeclarator).forEach((path) => {
40
+ const { id } = path.node;
41
+ id.type === "Identifier" && id.name === name && path.node.init && (init = path.node.init);
42
+ }), init;
43
+ }
44
+ function getExportSpecifierName(exported) {
45
+ return exported.type === "Identifier" || exported.type === "JSXIdentifier" ? exported.name : null;
46
+ }
47
+ function getNamedExportInit(j, exportName, filePath, visited = /* @__PURE__ */ new Set()) {
48
+ if (visited.has(filePath))
49
+ return null;
50
+ visited.add(filePath);
51
+ const root = parseModule(j, filePath);
52
+ if (!root)
53
+ return null;
54
+ for (const path of root.find(j.ExportNamedDeclaration).paths()) {
55
+ const { declaration, specifiers, source } = path.node, reexportSource = typeof source?.value == "string" ? source.value : null;
56
+ if (declaration?.type === "VariableDeclaration") {
57
+ for (const declarator of declaration.declarations)
58
+ if (declarator.type === "VariableDeclarator" && declarator.id.type === "Identifier" && declarator.id.name === exportName && declarator.init)
59
+ return { init: declarator.init, modulePath: filePath };
60
+ }
61
+ for (const spec of specifiers ?? []) {
62
+ if (spec.type !== "ExportSpecifier")
63
+ continue;
64
+ const exported = getExportSpecifierName(spec.exported);
65
+ if (!exported || exported !== exportName)
66
+ continue;
67
+ const local = spec.local?.type === "Identifier" ? spec.local.name : exported;
68
+ if (reexportSource) {
69
+ const resolvedPath = resolveRelativeModulePath(filePath, reexportSource);
70
+ if (resolvedPath) {
71
+ const followed = getNamedExportInit(j, local, resolvedPath, visited);
72
+ if (followed)
73
+ return followed;
74
+ }
75
+ continue;
76
+ }
77
+ const init = findVariableInit(j, root, local);
78
+ if (init)
79
+ return { init, modulePath: filePath };
80
+ }
81
+ }
82
+ return null;
83
+ }
6
84
  function getStyledComponentName(node) {
7
85
  if (!node || typeof node != "object" || !("type" in node))
8
86
  return null;
@@ -19,7 +97,7 @@ function getStyledComponentName(node) {
19
97
  }
20
98
  return current.type === "MemberExpression" && current.object ? getStyledComponentName(current.object) : null;
21
99
  }
22
- function getStyledComponentAliases(j, root, localNames) {
100
+ function getSameFileStyledComponentAliases(j, root, localNames) {
23
101
  const aliases = /* @__PURE__ */ new Set(), names = new Set(localNames);
24
102
  return root.find(j.VariableDeclarator).forEach((path) => {
25
103
  const { id, init } = path.node;
@@ -29,35 +107,72 @@ function getStyledComponentAliases(j, root, localNames) {
29
107
  baseComponent && names.has(baseComponent) && aliases.add(id.name);
30
108
  }), aliases;
31
109
  }
32
- const DEFAULT_WARNING = "Please double check styled-component migration(s) below";
110
+ function getImportedStyledComponentAliases(j, root, componentName, filePath, options) {
111
+ const aliases = /* @__PURE__ */ new Set();
112
+ return filePath && root.find(j.ImportDeclaration).forEach((path) => {
113
+ const source = path.node.source.value;
114
+ if (typeof source != "string")
115
+ return;
116
+ const resolvedPath = resolveRelativeModulePath(filePath, source);
117
+ if (resolvedPath)
118
+ for (const spec of path.node.specifiers ?? []) {
119
+ if (spec.type !== "ImportSpecifier" || "importKind" in spec && spec.importKind === "type" || spec.imported.type !== "Identifier")
120
+ continue;
121
+ const exportName = spec.imported.name, localName = spec.local?.type === "Identifier" ? spec.local.name : exportName, namedExport = getNamedExportInit(j, exportName, resolvedPath);
122
+ if (!namedExport)
123
+ continue;
124
+ const sourceRoot = parseModule(j, namedExport.modulePath);
125
+ if (!sourceRoot)
126
+ continue;
127
+ const exportLocalNames = getComponentLocalNames(j, sourceRoot, componentName, options), baseComponent = getStyledComponentName(namedExport.init);
128
+ baseComponent && exportLocalNames.has(baseComponent) && aliases.add(localName);
129
+ }
130
+ }), aliases;
131
+ }
132
+ function getStyledComponentAliases(j, root, componentName, filePath, localNames, options) {
133
+ const sameFile = getSameFileStyledComponentAliases(j, root, localNames), imported = getImportedStyledComponentAliases(j, root, componentName, filePath, options);
134
+ return /* @__PURE__ */ new Set([...sameFile, ...imported]);
135
+ }
136
+ const DEFAULT_WARNING = "Please double check styled-component migration below";
33
137
  function transformStyledComponents(j, root, aliases, filter, options = {}) {
34
- const names = new Set(aliases), { callback, warning = DEFAULT_WARNING } = options, aliasesToWarn = /* @__PURE__ */ new Set();
138
+ const names = new Set(aliases), { callback, warning = DEFAULT_WARNING } = options;
35
139
  let hasChanges = !1;
36
140
  return root.find(j.JSXOpeningElement).forEach((path) => {
37
141
  const name = path.node.name;
38
142
  if (name.type !== "JSXIdentifier" || !names.has(name.name))
39
143
  return;
40
144
  const attrs = path.node.attributes ?? [];
41
- filter(attrs) || aliasesToWarn.add(name.name), filter(attrs) && callback?.(path) && (hasChanges = !0);
42
- }), root.find(j.VariableDeclarator).forEach((path) => {
43
- const { id } = path.node;
44
- id.type === "Identifier" && aliasesToWarn.has(id.name) && insertTodoWarning(j, path, warning) && (hasChanges = !0);
145
+ if (!filter(attrs)) {
146
+ insertTodoWarning(j, path, warning) && (hasChanges = !0);
147
+ return;
148
+ }
149
+ callback?.(path) && (hasChanges = !0);
45
150
  }), hasChanges;
46
151
  }
47
- const BOX_TODO_WARNING = "Please double check the Box migration below", FLEX_TODO_WARNING = "Please double check the Flex migration below", GRID_TODO_WARNING = "Please double check the Grid migration below", STYLED_TODO_WARNING = "Please double check styled(Box) migration(s) below";
152
+ const BOX_TODO_WARNING = "Please double check the Box migration below", FLEX_TODO_WARNING = "Please double check the Flex migration below", GRID_TODO_WARNING = "Please double check the Grid migration below", STYLED_TODO_WARNING = "Please double check styled(Box) migration below";
48
153
  function transform(fileInfo, api, options) {
49
154
  const { fromPackage, toPackage } = options || {};
50
155
  return transformComponent(fileInfo, api, ({ j, root, markChanged }) => {
51
- const localNames = getComponentLocalNames(j, root, "Box", options), styledAliases = getStyledComponentAliases(j, root, localNames);
52
- if (!shouldTransformComponent(j, root, "Box", localNames, options))
156
+ const localNames = getComponentLocalNames(j, root, "Box", options), styledAliases = getStyledComponentAliases(
157
+ j,
158
+ root,
159
+ "Box",
160
+ fileInfo.path,
161
+ localNames,
162
+ options
163
+ );
164
+ if (!shouldTransformComponent(j, root, "Box", localNames, options, styledAliases))
53
165
  return;
54
- const replaceWithFlex = (attrs) => {
55
- const display = getStaticAttributeExpression(j, attrs, "display");
56
- return display === "flex" || display == "inline-flex";
57
- }, replaceWithGrid = (attrs) => {
58
- const display = getStaticAttributeExpression(j, attrs, "display");
59
- return display === "grid" || display == "inline-grid";
60
- };
166
+ const matchesDisplayValue = (attrs, suffix, exclude) => {
167
+ const display = getStaticAttributeExpression(j, attrs, "display") || [], values = Array.isArray(display) ? display : [display];
168
+ let matchesExclude;
169
+ for (const excluded of exclude)
170
+ if (values.some((value) => typeof value == "string" && value.endsWith(excluded))) {
171
+ matchesExclude = !0;
172
+ break;
173
+ }
174
+ return matchesExclude ? !1 : values.some((value) => typeof value == "string" && value.endsWith(suffix));
175
+ }, replaceWithFlex = (attrs) => matchesDisplayValue(attrs, "flex", ["block", "inline", "grid"]), replaceWithGrid = (attrs) => matchesDisplayValue(attrs, "grid", ["block", "inline", "flex"]);
61
176
  transformImport(j, root, "Box", fromPackage, toPackage) && markChanged(), replaceElement(
62
177
  j,
63
178
  root,
@@ -1 +1 @@
1
- {"version":3,"file":"box.js","sources":["../../../../src/utils/getStyledComponentName.ts","../../../../src/utils/getStyledComponentAliases.ts","../../../../src/utils/transformStyledComponents.ts","../../../../src/transforms/latest/box/box.ts"],"sourcesContent":["export function getStyledComponentName(node: unknown): string | null {\n if (!node || typeof node !== 'object' || !('type' in node)) {\n return null\n }\n\n const current = node as {\n type: string\n callee?: unknown\n tag?: unknown\n object?: unknown\n arguments?: unknown[]\n }\n\n if (current.type === 'TaggedTemplateExpression') {\n return getStyledComponentName(current.tag)\n }\n\n if (current.type === 'CallExpression') {\n const callee = current.callee\n\n if (\n callee &&\n typeof callee === 'object' &&\n 'type' in callee &&\n callee.type === 'Identifier' &&\n 'name' in callee &&\n callee.name === 'styled'\n ) {\n const arg = current.arguments?.[0]\n\n if (\n arg &&\n typeof arg === 'object' &&\n 'type' in arg &&\n arg.type === 'Identifier' &&\n 'name' in arg\n ) {\n return arg.name as string\n }\n\n return null\n }\n\n return getStyledComponentName(callee)\n }\n\n if (current.type === 'MemberExpression' && current.object) {\n return getStyledComponentName(current.object)\n }\n\n return null\n}\n","import type {API, Collection} from 'jscodeshift'\n\nimport {getStyledComponentName} from './getStyledComponentName'\n\nexport function getStyledComponentAliases(\n j: API['jscodeshift'],\n root: Collection,\n localNames: Iterable<string>,\n): Set<string> {\n const aliases = new Set<string>()\n const names = new Set(localNames)\n\n root.find(j.VariableDeclarator).forEach((path) => {\n const {id, init} = path.node\n\n if (!init || id.type !== 'Identifier') {\n return\n }\n\n const baseComponent = getStyledComponentName(init)\n\n if (baseComponent && names.has(baseComponent)) {\n aliases.add(id.name)\n }\n })\n\n return aliases\n}\n","import type {API, ASTPath, JSXAttribute, JSXOpeningElement, JSXSpreadAttribute} from 'jscodeshift'\n\nimport {insertTodoWarning} from './insertTodoWarning'\n\nconst DEFAULT_WARNING = 'Please double check styled-component migration(s) below'\n\n/**\n * Runs callback on JSX styled-component if `filter` passes. Otherwise, adds\n * a TODO on the styled definition if manual review is needed.\n * Returns whether the AST was updated.\n */\nexport function transformStyledComponents(\n j: API['jscodeshift'],\n root: ReturnType<API['jscodeshift']>,\n aliases: Iterable<string>,\n filter: (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => boolean,\n options: {\n callback?: (path: ASTPath<JSXOpeningElement>) => boolean | void\n warning?: string\n } = {},\n): boolean {\n const names = new Set(aliases)\n const {callback, warning = DEFAULT_WARNING} = options\n const aliasesToWarn = new Set<string>()\n let hasChanges = false\n\n root.find(j.JSXOpeningElement).forEach((path) => {\n const name = path.node.name\n\n if (name.type !== 'JSXIdentifier' || !names.has(name.name)) {\n return\n }\n\n const attrs = path.node.attributes ?? []\n\n if (!filter(attrs)) {\n aliasesToWarn.add(name.name)\n }\n\n if (filter(attrs)) {\n if (callback?.(path)) {\n hasChanges = true\n }\n }\n })\n\n root.find(j.VariableDeclarator).forEach((path) => {\n const {id} = path.node\n\n if (id.type === 'Identifier' && aliasesToWarn.has(id.name)) {\n if (insertTodoWarning(j, path, warning)) {\n hasChanges = true\n }\n }\n })\n\n return hasChanges\n}\n","import {type API, type FileInfo, type JSXSpreadAttribute, type JSXAttribute} from 'jscodeshift'\n\nimport type {BaseOptions} from '../../../types/BaseOptions'\nimport {getComponentLocalNames} from '../../../utils/getComponentLocalNames'\nimport {getStaticAttributeExpression} from '../../../utils/getStaticAttributeExpression'\nimport {getStyledComponentAliases} from '../../../utils/getStyledComponentAliases'\nimport {replaceElement} from '../../../utils/replaceElement'\nimport {shouldTransformComponent} from '../../../utils/shouldTransformComponent'\nimport {transformAttributes} from '../../../utils/transformAttributes'\nimport {transformComponent} from '../../../utils/transformComponent'\nimport {transformImport} from '../../../utils/transformImport'\nimport {transformStyledComponents} from '../../../utils/transformStyledComponents'\nimport {FLEX_MODS} from '../flex/flex.mods'\nimport {GRID_MODS} from '../grid/grid.mods'\nimport {BOX_MODS} from './box.mods'\n\nconst BOX_TODO_WARNING = 'Please double check the Box migration below'\nconst FLEX_TODO_WARNING = 'Please double check the Flex migration below'\nconst GRID_TODO_WARNING = 'Please double check the Grid migration below'\nconst STYLED_TODO_WARNING = 'Please double check styled(Box) migration(s) below'\n\n/** @internal */\nexport default function transform(\n fileInfo: FileInfo,\n api: API,\n options?: BaseOptions,\n): string | undefined {\n const {fromPackage, toPackage} = options || {}\n\n return transformComponent(fileInfo, api, ({j, root, markChanged}) => {\n const localNames = getComponentLocalNames(j, root, 'Box', options)\n const styledAliases = getStyledComponentAliases(j, root, localNames)\n\n if (!shouldTransformComponent(j, root, 'Box', localNames, options)) {\n return\n }\n\n const replaceWithFlex = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => {\n const display = getStaticAttributeExpression(j, attrs, 'display')\n return display === 'flex' || display == 'inline-flex'\n }\n\n const replaceWithGrid = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => {\n const display = getStaticAttributeExpression(j, attrs, 'display')\n return display === 'grid' || display == 'inline-grid'\n }\n\n if (transformImport(j, root, 'Box', fromPackage, toPackage)) {\n markChanged()\n }\n\n if (\n replaceElement(\n j,\n root,\n replaceWithFlex,\n {\n element: 'Box',\n localNames,\n },\n {\n element: 'Flex',\n callback: (path) => transformAttributes(j, path, FLEX_MODS, FLEX_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n\n if (\n replaceElement(\n j,\n root,\n replaceWithGrid,\n {\n element: 'Box',\n localNames,\n callback: (path) => transformAttributes(j, path, BOX_MODS, BOX_TODO_WARNING),\n },\n {\n element: 'Grid',\n callback: (path) => transformAttributes(j, path, GRID_MODS, GRID_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n\n if (\n transformStyledComponents(\n j,\n root,\n styledAliases,\n (attrs) => !replaceWithFlex(attrs) && !replaceWithGrid(attrs),\n {\n warning: STYLED_TODO_WARNING,\n callback: (path) => transformAttributes(j, path, BOX_MODS, BOX_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n })\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"box.js","sources":["../../../../src/utils/parseModule.ts","../../../../src/utils/resolveRelativeModulePath.ts","../../../../src/utils/getNamedExportInit.ts","../../../../src/utils/getStyledComponentName.ts","../../../../src/utils/getStyledComponentAliases.ts","../../../../src/utils/transformStyledComponents.ts","../../../../src/transforms/latest/box/box.ts"],"sourcesContent":["import {readFileSync} from 'node:fs'\n\nimport type {API, Collection} from 'jscodeshift'\n\nconst parseCache = new Map<string, Collection>()\n\nexport function parseModule(j: API['jscodeshift'], filePath: string): Collection | null {\n const cached = parseCache.get(filePath)\n\n if (cached) {\n return cached\n }\n\n try {\n const source = readFileSync(filePath, 'utf8')\n const root = j(source)\n\n parseCache.set(filePath, root)\n\n return root\n } catch {\n return null\n }\n}\n\n/** @internal */\nexport function clearModuleParseCache(): void {\n parseCache.clear()\n}\n","import {existsSync} from 'node:fs'\nimport {dirname, join, resolve} from 'node:path'\n\nconst EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'] as const\n\nexport function resolveRelativeModulePath(\n fromFilePath: string,\n moduleSpecifier: string,\n): string | null {\n if (!moduleSpecifier.startsWith('.')) {\n return null\n }\n\n const base = resolve(dirname(fromFilePath), moduleSpecifier)\n\n for (const ext of EXTENSIONS) {\n const filePath = `${base}${ext}`\n\n if (existsSync(filePath)) {\n return filePath\n }\n }\n\n for (const ext of EXTENSIONS) {\n const indexPath = join(base, `index${ext}`)\n\n if (existsSync(indexPath)) {\n return indexPath\n }\n }\n\n if (existsSync(base)) {\n return base\n }\n\n return null\n}\n","import type {API, Expression, ExportSpecifier} from 'jscodeshift'\n\nimport {parseModule} from './parseModule'\nimport {resolveRelativeModulePath} from './resolveRelativeModulePath'\n\nexport type NamedExportInit = {\n init: Expression\n modulePath: string\n}\n\nfunction findVariableInit(\n j: API['jscodeshift'],\n root: ReturnType<API['jscodeshift']>,\n name: string,\n): Expression | null {\n let init: Expression | null = null\n\n root.find(j.VariableDeclarator).forEach((path) => {\n const {id} = path.node\n\n if (id.type === 'Identifier' && id.name === name && path.node.init) {\n init = path.node.init\n }\n })\n\n return init\n}\n\nfunction getExportSpecifierName(exported: ExportSpecifier['exported']): string | null {\n if (exported.type === 'Identifier' || exported.type === 'JSXIdentifier') {\n return exported.name\n }\n\n return null\n}\n\nexport function getNamedExportInit(\n j: API['jscodeshift'],\n exportName: string,\n filePath: string,\n visited: Set<string> = new Set(),\n): NamedExportInit | null {\n if (visited.has(filePath)) {\n return null\n }\n\n visited.add(filePath)\n\n const root = parseModule(j, filePath)\n\n if (!root) {\n return null\n }\n\n for (const path of root.find(j.ExportNamedDeclaration).paths()) {\n const {declaration, specifiers, source} = path.node\n const reexportSource = typeof source?.value === 'string' ? source.value : null\n\n if (declaration?.type === 'VariableDeclaration') {\n for (const declarator of declaration.declarations) {\n if (declarator.type !== 'VariableDeclarator') {\n continue\n }\n\n if (\n declarator.id.type === 'Identifier' &&\n declarator.id.name === exportName &&\n declarator.init\n ) {\n return {init: declarator.init, modulePath: filePath}\n }\n }\n }\n\n for (const spec of specifiers ?? []) {\n if (spec.type !== 'ExportSpecifier') {\n continue\n }\n\n const exported = getExportSpecifierName(spec.exported)\n\n if (!exported || exported !== exportName) {\n continue\n }\n\n const local = spec.local?.type === 'Identifier' ? spec.local.name : exported\n\n if (reexportSource) {\n const resolvedPath = resolveRelativeModulePath(filePath, reexportSource)\n\n if (resolvedPath) {\n const followed = getNamedExportInit(j, local, resolvedPath, visited)\n\n if (followed) {\n return followed\n }\n }\n\n continue\n }\n\n const init = findVariableInit(j, root, local)\n\n if (init) {\n return {init, modulePath: filePath}\n }\n }\n }\n\n return null\n}\n","export function getStyledComponentName(node: unknown): string | null {\n if (!node || typeof node !== 'object' || !('type' in node)) {\n return null\n }\n\n const current = node as {\n type: string\n callee?: unknown\n tag?: unknown\n object?: unknown\n arguments?: unknown[]\n }\n\n if (current.type === 'TaggedTemplateExpression') {\n return getStyledComponentName(current.tag)\n }\n\n if (current.type === 'CallExpression') {\n const callee = current.callee\n\n if (\n callee &&\n typeof callee === 'object' &&\n 'type' in callee &&\n callee.type === 'Identifier' &&\n 'name' in callee &&\n callee.name === 'styled'\n ) {\n const arg = current.arguments?.[0]\n\n if (\n arg &&\n typeof arg === 'object' &&\n 'type' in arg &&\n arg.type === 'Identifier' &&\n 'name' in arg\n ) {\n return arg.name as string\n }\n\n return null\n }\n\n return getStyledComponentName(callee)\n }\n\n if (current.type === 'MemberExpression' && current.object) {\n return getStyledComponentName(current.object)\n }\n\n return null\n}\n","import type {API, Collection} from 'jscodeshift'\n\nimport type {BaseOptions} from '../types/BaseOptions'\nimport {getComponentLocalNames} from './getComponentLocalNames'\nimport {getNamedExportInit} from './getNamedExportInit'\nimport {getStyledComponentName} from './getStyledComponentName'\nimport {parseModule} from './parseModule'\nimport {resolveRelativeModulePath} from './resolveRelativeModulePath'\n\nexport function getSameFileStyledComponentAliases(\n j: API['jscodeshift'],\n root: Collection,\n localNames: Iterable<string>,\n): Set<string> {\n const aliases = new Set<string>()\n const names = new Set(localNames)\n\n root.find(j.VariableDeclarator).forEach((path) => {\n const {id, init} = path.node\n\n if (!init || id.type !== 'Identifier') {\n return\n }\n\n const baseComponent = getStyledComponentName(init)\n\n if (baseComponent && names.has(baseComponent)) {\n aliases.add(id.name)\n }\n })\n\n return aliases\n}\n\nexport function getImportedStyledComponentAliases(\n j: API['jscodeshift'],\n root: Collection,\n componentName: string,\n filePath: string | undefined,\n options?: BaseOptions,\n): Set<string> {\n const aliases = new Set<string>()\n\n if (!filePath) {\n return aliases\n }\n\n root.find(j.ImportDeclaration).forEach((path) => {\n const source = path.node.source.value\n\n if (typeof source !== 'string') {\n return\n }\n\n const resolvedPath = resolveRelativeModulePath(filePath, source)\n\n if (!resolvedPath) {\n return\n }\n\n for (const spec of path.node.specifiers ?? []) {\n if (spec.type !== 'ImportSpecifier') {\n continue\n }\n\n if ('importKind' in spec && spec.importKind === 'type') {\n continue\n }\n\n if (spec.imported.type !== 'Identifier') {\n continue\n }\n\n const exportName = spec.imported.name\n const localName = spec.local?.type === 'Identifier' ? spec.local.name : exportName\n const namedExport = getNamedExportInit(j, exportName, resolvedPath)\n\n if (!namedExport) {\n continue\n }\n\n const sourceRoot = parseModule(j, namedExport.modulePath)\n\n if (!sourceRoot) {\n continue\n }\n\n const exportLocalNames = getComponentLocalNames(j, sourceRoot, componentName, options)\n const baseComponent = getStyledComponentName(namedExport.init)\n\n if (baseComponent && exportLocalNames.has(baseComponent)) {\n aliases.add(localName)\n }\n }\n })\n\n return aliases\n}\n\nexport function getStyledComponentAliases(\n j: API['jscodeshift'],\n root: Collection,\n componentName: string,\n filePath: string | undefined,\n localNames: Set<string>,\n options?: BaseOptions,\n): Set<string> {\n const sameFile = getSameFileStyledComponentAliases(j, root, localNames)\n const imported = getImportedStyledComponentAliases(j, root, componentName, filePath, options)\n\n return new Set([...sameFile, ...imported])\n}\n","import type {API, ASTPath, JSXAttribute, JSXOpeningElement, JSXSpreadAttribute} from 'jscodeshift'\n\nimport {insertTodoWarning} from './insertTodoWarning'\n\nconst DEFAULT_WARNING = 'Please double check styled-component migration below'\n\n/**\n * Runs callback on JSX styled-component if `filter` passes. Otherwise, adds\n * a TODO on the component JSX instance when manual review is needed.\n * Returns whether the AST was updated.\n */\nexport function transformStyledComponents(\n j: API['jscodeshift'],\n root: ReturnType<API['jscodeshift']>,\n aliases: Iterable<string>,\n filter: (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => boolean,\n options: {\n callback?: (path: ASTPath<JSXOpeningElement>) => boolean | void\n warning?: string\n } = {},\n): boolean {\n const names = new Set(aliases)\n const {callback, warning = DEFAULT_WARNING} = options\n let hasChanges = false\n\n root.find(j.JSXOpeningElement).forEach((path) => {\n const name = path.node.name\n\n if (name.type !== 'JSXIdentifier' || !names.has(name.name)) {\n return\n }\n\n const attrs = path.node.attributes ?? []\n\n if (!filter(attrs)) {\n if (insertTodoWarning(j, path, warning)) {\n hasChanges = true\n }\n\n return\n }\n\n if (callback?.(path)) {\n hasChanges = true\n }\n })\n\n return hasChanges\n}\n","import {type API, type FileInfo, type JSXSpreadAttribute, type JSXAttribute} from 'jscodeshift'\n\nimport type {BaseOptions} from '../../../types/BaseOptions'\nimport {getComponentLocalNames} from '../../../utils/getComponentLocalNames'\nimport {getStaticAttributeExpression} from '../../../utils/getStaticAttributeExpression'\nimport {getStyledComponentAliases} from '../../../utils/getStyledComponentAliases'\nimport {replaceElement} from '../../../utils/replaceElement'\nimport {shouldTransformComponent} from '../../../utils/shouldTransformComponent'\nimport {transformAttributes} from '../../../utils/transformAttributes'\nimport {transformComponent} from '../../../utils/transformComponent'\nimport {transformImport} from '../../../utils/transformImport'\nimport {transformStyledComponents} from '../../../utils/transformStyledComponents'\nimport {FLEX_MODS} from '../flex/flex.mods'\nimport {GRID_MODS} from '../grid/grid.mods'\nimport {BOX_MODS} from './box.mods'\n\nconst BOX_TODO_WARNING = 'Please double check the Box migration below'\nconst FLEX_TODO_WARNING = 'Please double check the Flex migration below'\nconst GRID_TODO_WARNING = 'Please double check the Grid migration below'\nconst STYLED_TODO_WARNING = 'Please double check styled(Box) migration below'\n\n/** @internal */\nexport default function transform(\n fileInfo: FileInfo,\n api: API,\n options?: BaseOptions,\n): string | undefined {\n const {fromPackage, toPackage} = options || {}\n\n return transformComponent(fileInfo, api, ({j, root, markChanged}) => {\n const localNames = getComponentLocalNames(j, root, 'Box', options)\n const styledAliases = getStyledComponentAliases(\n j,\n root,\n 'Box',\n fileInfo.path,\n localNames,\n options,\n )\n\n if (!shouldTransformComponent(j, root, 'Box', localNames, options, styledAliases)) {\n return\n }\n\n const matchesDisplayValue = (\n attrs: (JSXAttribute | JSXSpreadAttribute)[],\n suffix: string,\n exclude: string[],\n ) => {\n const display = getStaticAttributeExpression(j, attrs, 'display') || []\n const values = Array.isArray(display) ? display : [display]\n let matchesExclude\n\n for (const excluded of exclude) {\n if (values.some((value) => typeof value === 'string' && value.endsWith(excluded))) {\n matchesExclude = true\n break\n }\n }\n\n if (matchesExclude) {\n return false\n }\n\n return values.some((value) => typeof value === 'string' && value.endsWith(suffix))\n }\n\n const replaceWithFlex = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) =>\n matchesDisplayValue(attrs, 'flex', ['block', 'inline', 'grid'])\n\n const replaceWithGrid = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) =>\n matchesDisplayValue(attrs, 'grid', ['block', 'inline', 'flex'])\n\n if (transformImport(j, root, 'Box', fromPackage, toPackage)) {\n markChanged()\n }\n\n if (\n replaceElement(\n j,\n root,\n replaceWithFlex,\n {\n element: 'Box',\n localNames,\n },\n {\n element: 'Flex',\n callback: (path) => transformAttributes(j, path, FLEX_MODS, FLEX_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n\n if (\n replaceElement(\n j,\n root,\n replaceWithGrid,\n {\n element: 'Box',\n localNames,\n callback: (path) => transformAttributes(j, path, BOX_MODS, BOX_TODO_WARNING),\n },\n {\n element: 'Grid',\n callback: (path) => transformAttributes(j, path, GRID_MODS, GRID_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n\n if (\n transformStyledComponents(\n j,\n root,\n styledAliases,\n (attrs) => !replaceWithFlex(attrs) && !replaceWithGrid(attrs),\n {\n warning: STYLED_TODO_WARNING,\n callback: (path) => transformAttributes(j, path, BOX_MODS, BOX_TODO_WARNING),\n },\n )\n ) {\n markChanged()\n }\n })\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui-codemod",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "ISC",
@@ -109,6 +109,18 @@ export const LAYOUT_MODS: AttributeMods = {
109
109
  none: 'auto',
110
110
  auto: 'auto',
111
111
  initial: 'auto',
112
+ 1: '0%',
113
+ 2: '0%',
114
+ 3: '0%',
115
+ 4: '0%',
116
+ 5: '0%',
117
+ 6: '0%',
118
+ 7: '0%',
119
+ 8: '0%',
120
+ 9: '0%',
121
+ 10: '0%',
122
+ 11: '0%',
123
+ 12: '0%',
112
124
  },
113
125
  },
114
126
  {
@@ -1,3 +1,12 @@
1
+ import {mkdtempSync, readFileSync, rmSync, writeFileSync} from 'node:fs'
2
+ import {tmpdir} from 'node:os'
3
+ import {join} from 'node:path'
4
+
5
+ import {afterEach, describe, expect, it} from 'vitest'
6
+
7
+ const applyTransform = require('jscodeshift/dist/testUtils').applyTransform
8
+
9
+ import {clearModuleParseCache} from '../../../utils/parseModule'
1
10
  import {defineInlineTest} from '../../../utils/testUtils'
2
11
  import transform from './box'
3
12
 
@@ -178,6 +187,38 @@ defineInlineTest(
178
187
  'replaces Box with Grid when display is grid',
179
188
  )
180
189
 
190
+ defineInlineTest(
191
+ transform,
192
+ {},
193
+ `
194
+ <Box display={['none', undefined, null, 'flex']} />
195
+ `,
196
+ `
197
+ <Flex display={['none', undefined, null, 'flex']} />
198
+ `,
199
+ 'replaces Box with Flex when display is an array with flex',
200
+ )
201
+
202
+ defineInlineTest(
203
+ transform,
204
+ {},
205
+ `
206
+ <>
207
+ <Box display={['block', undefined, null, 'flex']} />
208
+ {/* This forces a change to avoid the transform return null */}
209
+ <Box display="flex" />
210
+ </>
211
+ `,
212
+ `
213
+ <>
214
+ <Box display={['block', undefined, null, 'flex']} />
215
+ {/* This forces a change to avoid the transform return null */}
216
+ <Flex display="flex" />
217
+ </>
218
+ `,
219
+ 'does not replace Box with Flex when display is a mixed array',
220
+ )
221
+
181
222
  defineInlineTest(
182
223
  transform,
183
224
  {},
@@ -194,11 +235,168 @@ defineInlineTest(
194
235
  import {Box} from '@sanity/ui'
195
236
 
196
237
  function Example() {
197
- // UI-CODEMOD TODO: Please double check styled(Box) migration(s) below
198
- const RootBox = styled(Box)(({theme}) => ({}));
238
+ const RootBox = styled(Box)(({theme}) => ({}))
199
239
 
200
- return <RootBox display="flex" alignItems="center" />
240
+ return (
241
+ // UI-CODEMOD TODO: Please double check styled(Box) migration below
242
+ <RootBox display="flex" alignItems="center" />
243
+ );
201
244
  }
202
245
  `,
203
- 'warns and does not transform attributes if styled Box definition should be replaced',
246
+ 'warns and does not transform attributes if styled Box should be replaced',
204
247
  )
248
+
249
+ const tempDirs: string[] = []
250
+
251
+ afterEach(() => {
252
+ clearModuleParseCache()
253
+
254
+ for (const dir of tempDirs.splice(0)) {
255
+ rmSync(dir, {recursive: true, force: true})
256
+ }
257
+ })
258
+
259
+ describe('cross-file styled aliases', () => {
260
+ it('transforms attributes on imported styled Box wrappers', () => {
261
+ const dir = mkdtempSync(join(tmpdir(), 'ui-codemod-box-crossfile-'))
262
+
263
+ tempDirs.push(dir)
264
+
265
+ writeFileSync(
266
+ join(dir, 'Component.styled.tsx'),
267
+ `
268
+ import {Box} from '@sanity/ui'
269
+
270
+ export const RootBox = styled(Box)(({theme}) => ({}))
271
+ `,
272
+ )
273
+
274
+ writeFileSync(
275
+ join(dir, 'Component.tsx'),
276
+ `
277
+ import {RootBox} from './Component.styled'
278
+
279
+ export function Component() {
280
+ return <RootBox alignItems="center" />
281
+ }
282
+ `,
283
+ )
284
+
285
+ const importerPath = join(dir, 'Component.tsx')
286
+ const source = readFileSync(importerPath, 'utf8')
287
+ const output = applyTransform(transform, {}, {source, path: importerPath}, {parser: 'tsx'})
288
+
289
+ expect(output).toContain('alignItems: "center"')
290
+ expect(output).not.toContain('<RootBox alignItems="center" />')
291
+ })
292
+
293
+ it('adds todo warning when imported styled Box wrapper should be replaced', () => {
294
+ const dir = mkdtempSync(join(tmpdir(), 'ui-codemod-box-crossfile-todo-'))
295
+
296
+ tempDirs.push(dir)
297
+
298
+ writeFileSync(
299
+ join(dir, 'Component.styled.tsx'),
300
+ `
301
+ import {Box} from '@sanity/ui'
302
+
303
+ export const RootBox = styled(Box)(({theme}) => ({}))
304
+ `,
305
+ )
306
+
307
+ writeFileSync(
308
+ join(dir, 'Component.tsx'),
309
+ `
310
+ import {RootBox} from './Component.styled'
311
+
312
+ export function Component() {
313
+ return <RootBox display="flex" alignItems="center" />
314
+ }
315
+ `,
316
+ )
317
+
318
+ const importerPath = join(dir, 'Component.tsx')
319
+ const source = readFileSync(importerPath, 'utf8')
320
+ const output = applyTransform(transform, {}, {source, path: importerPath}, {parser: 'tsx'})
321
+
322
+ expect(output).toContain('UI-CODEMOD TODO: Please double check styled(Box) migration below')
323
+ expect(output).toContain('<RootBox display="flex" alignItems="center" />')
324
+ expect(output).not.toContain('const RootBox = styled(Box)')
325
+ })
326
+
327
+ it('does not rewrite unrelated Box from another package', () => {
328
+ const dir = mkdtempSync(join(tmpdir(), 'ui-codemod-box-crossfile-unrelated-'))
329
+
330
+ tempDirs.push(dir)
331
+
332
+ writeFileSync(
333
+ join(dir, 'Component.styled.tsx'),
334
+ `
335
+ import {Box} from '@sanity/ui'
336
+
337
+ export const RootBox = styled(Box)(({theme}) => ({}))
338
+ `,
339
+ )
340
+
341
+ writeFileSync(
342
+ join(dir, 'Component.tsx'),
343
+ `
344
+ import {Box} from 'another-package'
345
+ import {RootBox} from './Component.styled'
346
+
347
+ export function Component() {
348
+ return (
349
+ <>
350
+ <RootBox alignItems="center" />
351
+ <Box display="flex" />
352
+ </>
353
+ )
354
+ }
355
+ `,
356
+ )
357
+
358
+ const importerPath = join(dir, 'Component.tsx')
359
+ const source = readFileSync(importerPath, 'utf8')
360
+ const output = applyTransform(transform, {}, {source, path: importerPath}, {parser: 'tsx'})
361
+
362
+ expect(output).toContain('alignItems: "center"')
363
+ expect(output).not.toContain('<RootBox alignItems="center" />')
364
+ expect(output).toContain('<Box display="flex" />')
365
+ expect(output).not.toContain('<Flex')
366
+ })
367
+
368
+ it('transforms styled Box wrappers imported through barrel re-exports', () => {
369
+ const dir = mkdtempSync(join(tmpdir(), 'ui-codemod-box-crossfile-barrel-'))
370
+
371
+ tempDirs.push(dir)
372
+
373
+ writeFileSync(
374
+ join(dir, 'Component.styled.tsx'),
375
+ `
376
+ import {Box} from '@sanity/ui'
377
+
378
+ export const RootBox = styled(Box)(({theme}) => ({}))
379
+ `,
380
+ )
381
+
382
+ writeFileSync(join(dir, 'index.ts'), `export {RootBox} from './Component.styled'`)
383
+
384
+ writeFileSync(
385
+ join(dir, 'Component.tsx'),
386
+ `
387
+ import {RootBox} from './index'
388
+
389
+ export function Component() {
390
+ return <RootBox alignItems="center" />
391
+ }
392
+ `,
393
+ )
394
+
395
+ const importerPath = join(dir, 'Component.tsx')
396
+ const source = readFileSync(importerPath, 'utf8')
397
+ const output = applyTransform(transform, {}, {source, path: importerPath}, {parser: 'tsx'})
398
+
399
+ expect(output).toContain('alignItems: "center"')
400
+ expect(output).not.toContain('<RootBox alignItems="center" />')
401
+ })
402
+ })
@@ -17,7 +17,7 @@ import {BOX_MODS} from './box.mods'
17
17
  const BOX_TODO_WARNING = 'Please double check the Box migration below'
18
18
  const FLEX_TODO_WARNING = 'Please double check the Flex migration below'
19
19
  const GRID_TODO_WARNING = 'Please double check the Grid migration below'
20
- const STYLED_TODO_WARNING = 'Please double check styled(Box) migration(s) below'
20
+ const STYLED_TODO_WARNING = 'Please double check styled(Box) migration below'
21
21
 
22
22
  /** @internal */
23
23
  export default function transform(
@@ -29,22 +29,48 @@ export default function transform(
29
29
 
30
30
  return transformComponent(fileInfo, api, ({j, root, markChanged}) => {
31
31
  const localNames = getComponentLocalNames(j, root, 'Box', options)
32
- const styledAliases = getStyledComponentAliases(j, root, localNames)
32
+ const styledAliases = getStyledComponentAliases(
33
+ j,
34
+ root,
35
+ 'Box',
36
+ fileInfo.path,
37
+ localNames,
38
+ options,
39
+ )
33
40
 
34
- if (!shouldTransformComponent(j, root, 'Box', localNames, options)) {
41
+ if (!shouldTransformComponent(j, root, 'Box', localNames, options, styledAliases)) {
35
42
  return
36
43
  }
37
44
 
38
- const replaceWithFlex = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => {
39
- const display = getStaticAttributeExpression(j, attrs, 'display')
40
- return display === 'flex' || display == 'inline-flex'
41
- }
45
+ const matchesDisplayValue = (
46
+ attrs: (JSXAttribute | JSXSpreadAttribute)[],
47
+ suffix: string,
48
+ exclude: string[],
49
+ ) => {
50
+ const display = getStaticAttributeExpression(j, attrs, 'display') || []
51
+ const values = Array.isArray(display) ? display : [display]
52
+ let matchesExclude
53
+
54
+ for (const excluded of exclude) {
55
+ if (values.some((value) => typeof value === 'string' && value.endsWith(excluded))) {
56
+ matchesExclude = true
57
+ break
58
+ }
59
+ }
60
+
61
+ if (matchesExclude) {
62
+ return false
63
+ }
42
64
 
43
- const replaceWithGrid = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => {
44
- const display = getStaticAttributeExpression(j, attrs, 'display')
45
- return display === 'grid' || display == 'inline-grid'
65
+ return values.some((value) => typeof value === 'string' && value.endsWith(suffix))
46
66
  }
47
67
 
68
+ const replaceWithFlex = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) =>
69
+ matchesDisplayValue(attrs, 'flex', ['block', 'inline', 'grid'])
70
+
71
+ const replaceWithGrid = (attrs: (JSXAttribute | JSXSpreadAttribute)[]) =>
72
+ matchesDisplayValue(attrs, 'grid', ['block', 'inline', 'flex'])
73
+
48
74
  if (transformImport(j, root, 'Box', fromPackage, toPackage)) {
49
75
  markChanged()
50
76
  }
@@ -0,0 +1,7 @@
1
+ export function getElementMatchNames(element: string, localNames?: Iterable<string>): Set<string> {
2
+ if (localNames === undefined) {
3
+ return new Set([element])
4
+ }
5
+
6
+ return new Set(localNames)
7
+ }
@@ -2,18 +2,6 @@ import type {AnyExpression} from '../types/AnyExpression'
2
2
  import type {AttributeMapping} from '../types/AttributeMods'
3
3
 
4
4
  export function getMappingValue(mapping: AttributeMapping, expr: AnyExpression) {
5
- let matchedVal
6
-
7
- for (const val in mapping) {
8
- if (expr.value === mapping[val]) {
9
- matchedVal = mapping[val]
10
- }
11
- }
12
-
13
- if (matchedVal) {
14
- return matchedVal
15
- }
16
-
17
5
  let key
18
6
 
19
7
  if (
@@ -34,18 +22,27 @@ export function getMappingValue(mapping: AttributeMapping, expr: AnyExpression)
34
22
  }
35
23
  }
36
24
 
37
- if (!key) {
38
- return
39
- }
25
+ if (key) {
26
+ if (Object.prototype.hasOwnProperty.call(mapping, key)) {
27
+ return mapping[key]
28
+ }
40
29
 
41
- if (Object.prototype.hasOwnProperty.call(mapping, key)) {
42
- return mapping[key]
43
- }
30
+ const number = Number(key)
44
31
 
45
- const number = Number(key)
32
+ if (!Number.isNaN(number) && Object.prototype.hasOwnProperty.call(mapping, number)) {
33
+ return mapping[number]
34
+ }
35
+ }
46
36
 
47
- if (!Number.isNaN(number) && Object.prototype.hasOwnProperty.call(mapping, number)) {
48
- return mapping[number]
37
+ if (
38
+ expr.type === 'BooleanLiteral' ||
39
+ (expr.type === 'Literal' && typeof expr.value === 'boolean')
40
+ ) {
41
+ for (const val in mapping) {
42
+ if (expr.value === mapping[val]) {
43
+ return mapping[val]
44
+ }
45
+ }
49
46
  }
50
47
 
51
48
  return