Skip to content

Commit

Permalink
use correct ast types
Browse files Browse the repository at this point in the history
  • Loading branch information
yyyyaaa committed Sep 11, 2024
1 parent f522dc6 commit 3b91a9f
Show file tree
Hide file tree
Showing 4 changed files with 449 additions and 59 deletions.
378 changes: 378 additions & 0 deletions packages/vue-codemod/__tests__/vue-codemod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,381 @@ it("should remove eventHandlers in a v-bind only containing itself in a spread",
const result = transformCode(source);
expect(trimWhitespace(result)).toBe(trimWhitespace(expected));
});

it.only("should add defineEmits to script setup if eventHandlers is present", () => {
const source = `
<template>
<Box
:boxRef="buttonRef"
:class="combinedClassName"
v-bind="{ ...spreadAttributes, ...eventHandlers }"
><Spinner
:size="iconSize"
:attributes="{
display:
isLoading && spinnerPlacement === 'start' ? 'inline-block' : 'none',
}"
></Spinner
><Icon
:name="leftIcon"
:size="iconSize"
:attributes="{
display: !!leftIcon && !isLoading ? 'inline-block' : 'none',
marginRight: !$slots.default ? '$0' : '$2',
}"
></Icon>
<template v-if="!isLoading">
<slot />
</template>
<Icon
:name="rightIcon"
:size="iconSize"
:attributes="{
display: !!rightIcon && !isLoading ? 'inline-block' : 'none',
marginLeft: !$slots.default ? '$0' : '$2',
}"
></Icon
><Spinner
:size="iconSize"
:attributes="{
display:
isLoading && spinnerPlacement === 'end' ? 'inline-block' : 'none',
}"
></Spinner
></Box>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from "vue";
import { assignInlineVars } from "@vanilla-extract/dynamic";
import clx from "clsx";
import Icon from "../icon";
import Box from "../box";
import Spinner from "../spinner";
import { store } from "../../models/store";
import { recipe, buttonOverrides } from "./button.helper";
import { isDefaultAccent, getAccentHover } from "../../helpers/style";
import { themeVars } from "../../styles/themes.css";
import { fullWidth, fullWidthHeight } from "../shared/shared.css";
import * as styles from "./button.css";
const props = withDefaults(defineProps(), {
intent: "primary",
size: "md",
as: "button",
variant: "solid",
disabled: undefined,
isLoading: undefined,
fluidWidth: undefined,
fluid: undefined,
className: undefined,
attributes: undefined,
domAttributes: undefined,
buttonRef: undefined,
iconSize: undefined,
spinnerPlacement: "start",
leftIcon: undefined,
children: undefined,
rightIcon: undefined,
});
const _overrideManager = ref(null);
const _theme = ref("light");
const _themeAccent = ref(null);
const cleanupRef = ref(null);
onMounted(() => {
const uiStore = getStoreState();
_theme.value = uiStore[0];
_themeAccent.value = uiStore[1];
_overrideManager.value = uiStore[2];
cleanupRef.value = store.subscribe((newState, prevState) => {
_theme.value = newState.theme;
_themeAccent.value = newState.themeAccent;
_overrideManager.value = newState.overrideStyleManager;
});
});
onUnmounted(() => {
if (typeof cleanupRef.value === "function") {
cleanupRef();
}
});
const combinedClassName = computed(() => {
return clx(
styles.buttonSize[props.size],
recipe({
as: props.as,
variant: props.variant,
intent: "primary",
isDisabled: props.disabled || props.isLoading,
theme: getStoreState().theme,
}),
props.fluidWidth ? fullWidth : null,
props.fluid ? fullWidthHeight : null,
props.className
);
});
const spreadAttributes = computed(() => {
return Object.assign(
{
as: props.as,
},
{
attributes: {
...props.attributes,
disabled: props.disabled,
// style: state.getVars(),
...props.domAttributes,
},
}
);
});
const eventHandlers = computed(() => {
const handlers = {};
const eventProps = [
"onClick",
"onDoubleClick",
"onMouseDown",
"onMouseUp",
"onMouseEnter",
"onMouseLeave",
"onMouseMove",
"onMouseOver",
"onMouseOut",
"onKeyDown",
"onKeyUp",
"onKeyPress",
"onFocus",
"onBlur",
"onInput",
"onChange",
"onSubmit",
"onReset",
"onScroll",
"onWheel",
"onDragStart",
"onDrag",
"onDragEnd",
"onDragEnter",
"onDragLeave",
"onDragOver",
"onDrop",
"onTouchStart",
"onTouchMove",
"onTouchEnd",
"onTouchCancel",
];
eventProps.forEach((eventName) => {
if (props.eventName) {
handlers[eventName] = (event) => props.eventName(event);
}
});
return handlers;
});
function getStoreState() {
// This seems weird but it's a workaround for one minor bug from mitosis
// If we have any variables in any function scope that has the same name as the store state, mitosis understands that it's the same variable
// and will attempt to transform those unwanted/unrelated variables into the ones in the state.<variable>
// So we need to name these values differently (e.g. _keyA: valueA) or inverse
return {
theme: store.getState().theme,
themeAccent: store.getState().themeAccent,
overrideStyleManager: store.getState().overrideStyleManager,
};
}
</script>
`;

const expected = `
<template>
<Box
:boxRef="buttonRef"
:class="combinedClassName"
v-bind="{ ...spreadAttributes, ...eventHandlers }"
><Spinner
:size="iconSize"
:attributes="{
display:
isLoading && spinnerPlacement === 'start' ? 'inline-block' : 'none',
}"
></Spinner
><Icon
:name="leftIcon"
:size="iconSize"
:attributes="{
display: !!leftIcon && !isLoading ? 'inline-block' : 'none',
marginRight: !$slots.default ? '$0' : '$2',
}"
></Icon>
<template v-if="!isLoading">
<slot />
</template>
<Icon
:name="rightIcon"
:size="iconSize"
:attributes="{
display: !!rightIcon && !isLoading ? 'inline-block' : 'none',
marginLeft: !$slots.default ? '$0' : '$2',
}"
></Icon
><Spinner
:size="iconSize"
:attributes="{
display:
isLoading && spinnerPlacement === 'end' ? 'inline-block' : 'none',
}"
></Spinner
></Box>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from "vue";
import { assignInlineVars } from "@vanilla-extract/dynamic";
import clx from "clsx";
import Icon from "../icon";
import Box from "../box";
import Spinner from "../spinner";
import { store } from "../../models/store";
import { recipe, buttonOverrides } from "./button.helper";
import { isDefaultAccent, getAccentHover } from "../../helpers/style";
import { themeVars } from "../../styles/themes.css";
import { fullWidth, fullWidthHeight } from "../shared/shared.css";
import * as styles from "./button.css";
const props = withDefaults(defineProps(), {
intent: "primary",
size: "md",
as: "button",
variant: "solid",
disabled: undefined,
isLoading: undefined,
fluidWidth: undefined,
fluid: undefined,
className: undefined,
attributes: undefined,
domAttributes: undefined,
buttonRef: undefined,
iconSize: undefined,
spinnerPlacement: "start",
leftIcon: undefined,
children: undefined,
rightIcon: undefined,
});
const _overrideManager = ref(null);
const _theme = ref("light");
const _themeAccent = ref(null);
const cleanupRef = ref(null);
onMounted(() => {
const uiStore = getStoreState();
_theme.value = uiStore[0];
_themeAccent.value = uiStore[1];
_overrideManager.value = uiStore[2];
cleanupRef.value = store.subscribe((newState, prevState) => {
_theme.value = newState.theme;
_themeAccent.value = newState.themeAccent;
_overrideManager.value = newState.overrideStyleManager;
});
});
onUnmounted(() => {
if (typeof cleanupRef.value === "function") {
cleanupRef();
}
});
const combinedClassName = computed(() => {
return clx(
styles.buttonSize[props.size],
recipe({
as: props.as,
variant: props.variant,
intent: "primary",
isDisabled: props.disabled || props.isLoading,
theme: getStoreState().theme,
}),
props.fluidWidth ? fullWidth : null,
props.fluid ? fullWidthHeight : null,
props.className
);
});
const spreadAttributes = computed(() => {
return Object.assign(
{
as: props.as,
},
{
attributes: {
...props.attributes,
disabled: props.disabled,
// style: state.getVars(),
...props.domAttributes,
},
}
);
});
const eventHandlers = computed(() => {
const handlers = {};
const eventProps = [
"onClick",
"onDoubleClick",
"onMouseDown",
"onMouseUp",
"onMouseEnter",
"onMouseLeave",
"onMouseMove",
"onMouseOver",
"onMouseOut",
"onKeyDown",
"onKeyUp",
"onKeyPress",
"onFocus",
"onBlur",
"onInput",
"onChange",
"onSubmit",
"onReset",
"onScroll",
"onWheel",
"onDragStart",
"onDrag",
"onDragEnd",
"onDragEnter",
"onDragLeave",
"onDragOver",
"onDrop",
"onTouchStart",
"onTouchMove",
"onTouchEnd",
"onTouchCancel",
];
eventProps.forEach((eventName) => {
if (props.eventName) {
handlers[eventName] = (event) => props.eventName(event);
}
});
return handlers;
});
function getStoreState() {
// This seems weird but it's a workaround for one minor bug from mitosis
// If we have any variables in any function scope that has the same name as the store state, mitosis understands that it's the same variable
// and will attempt to transform those unwanted/unrelated variables into the ones in the state.<variable>
// So we need to name these values differently (e.g. _keyA: valueA) or inverse
return {
theme: store.getState().theme,
themeAccent: store.getState().themeAccent,
overrideStyleManager: store.getState().overrideStyleManager,
};
}
</script>
`;
const result = transformCode(source);
expect(trimWhitespace(result)).toBe(trimWhitespace(expected));
});
1 change: 1 addition & 0 deletions packages/vue-codemod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dist"
],
"dependencies": {
"ast-types": "^0.14.2",
"lodash": "^4.17.21",
"vue-metamorph": "^3.1.16"
},
Expand Down
Loading

0 comments on commit 3b91a9f

Please sign in to comment.