vue-class-style-codemod

Vue3 Migration Tool

MIT License

Stars
10
Committers
2

vue-class-style-codemod

This project is based on the fork of https://github.com/originjs/vue-codemod and was modified and further developed by Deutsche Bahn Fernverkehr AG.

Cloned from https://github.com/originjs/vue-codemod

vue-class-style-codemod is a Vue 2 to Vue 3 migration tool based on vue-codemod adding support to convert Vue Class-based components defined with vue-class-component and vue-property-decorator to the script setup component syntax.

How to use

Install all dependencies and make vue-class-style-codemod available globally npm run setup (Please use only npm and not yarn)

Transformation of projects and individual files with the help of vue-class-style-codemod.

vue-class-style-codemod -t/-a [transformation params]

or

npx vue-class-style-codemod -t/-a [transformation params]

  1. indicates the relative path of execution, which can be files and folders. If the file to be converted is in the same folder as the converter, you must specify the file starting from the parent folder.
  2. -a (or --runAllTransformation) means executing all rules.
  3. -t (or --transformation) means specific rule (ex. property-decorator)

Please note that the transformation completely replaces the old file

Rules

Having already the rule vue-class-component-v8 available in vue-class-style-codemod, this projects adds the two rules property-decorator and define-component-to-script-setup.

property-decorator

The rule converts most decorators of vue-property-decorator. The following table shows some exemplary conversions.

Class

import { Vue, Component } from 'vue-property-decorator';

@Component
export default class CreatedInput extends Vue {
  public created(): void {
    this.groupControls.register(this.id);
  }
  public beforeCreated(): void {
    this.groupControls.register(this.id);
  }
}
groupControls.register(id);
groupControls.register(id);

Emit

@Emit('reset')
resetCount() {
  this.count = 0;
}
const emit = defineEmits<{
(e: 'reset'): void
}>();

function resetCount() {
    count = 0;
    emit("reset");
}

Injection

@Inject() readonly foo!: string
@Inject('bar') readonly bar!: string
@Inject({ from: 'optional', default: 'default' }) readonly optional!: string
@Inject(symbol) readonly baz!: string
@Inject({ from: 'optional', default: () => 'Hello World' }) readonly optional!: string
const foo: string = inject('foo');
const bar: string = inject('bar');
const optional: string = inject('optional', 'default');
const baz: string = inject(symbol);
const optional: string = inject('optional', () => 'Hello World');

Next tick

await this.$nextTick();
await nextTick();

Gettext

public get cancelButtonText(): string {
  // Cancel button text comment
  return this.$gettext('Cancel');
}
const cancelButtonText = computed<string>(() => {
  // Cancel button translation comment
  return $gettext('Cancel');
});

Prop

@Prop(Number) readonly propA: number | undefined
@Prop({ default: 'default value' }) readonly propB: string
@Prop({ type: String }) readonly propC!: string
@Prop([String, Boolean]) readonly propD: string | boolean | undefined
@Prop(Number) readonly propE!: number | undefined
@Prop([String, Boolean]) readonly propF!: string | boolean | undefined
const props = withDefaults(defineProps<{
    propA?: number | undefined,
    propB?: string,
    propC: string,
    propD?: string | boolean | undefined,
    propE: number | undefined,
    propF: string | boolean | undefined
}>(), {
    propB: 'default value'
});

Provide

@Provide() foo = 'foo';
@Provide('bar') baz = 'bar';
const foo = reactive('foo');
provide('foo', foo);
const baz = reactive('bar');
provide('bar', baz);

Ref

@Ref() readonly anotherComponent!: AnotherComponent
@Ref('aButton') readonly button!: HTMLButtonElement
const anotherComponent = ref<AnotherComponent>();
const aButton = ref<HTMLButtonElement>();

Static class fields

export class AnyComponent extends Vue {
  private static readonly noScrollClass: string = 'util__noscroll-xs';

  public static addNoScrollXs(): void {
      document.documentElement.classList.add(AnyComponent.noScrollClass);
  }
}
const noScrollClass: string = 'util__noscroll-xs';

function addNoScrollXs(): void {
    document.documentElement.classList.add(noScrollClass);
}

Router

methodA() {
  return this.$route.params.id
}

methodB() {
  return this.$router.back()
}
const router = useRouter();
const route = useRoute();

function methodA() {
    return route.params.id;
}

function methodB() {
    return router.back();
}

Vuex

methodC() {
  return this.$store.commit('increment')
}
function methodC() {
    return store.commit('increment');
}

Watch

@Watch('person', { immediate: true, deep: true })
onPersonChanged(val: Person, oldVal: Person) {}
function onPersonChanged(val: Person, oldVal: Person) {}
watch(person, onPersonChanged);

define-component-to-script-setup

This rule converts the defineComponent syntax to the script setup syntax.

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'test',
  components: {
    testComponent
  },
  emit: ['change','input'],
  props: {
    counter: {
      required: false,
      type: Boolean
    },
  },
  setup(props, ctx) {
    ctx.emit('changeValue');
   	return {
    	test,
      title: computed(() => 'Hello World'),
    }
  },

  methods: {
    doIt() {
      console.log(`Hello ${this.name}`);
    },
  },

});
</script>
<script setup lang="ts">
  const emit = defineEmits<{
    (e: 'change'): void,
    (e: 'input'): void
  }>();

  const props = defineProps<{
    counter?: Boolean
  }>();

  const title = computed(() => 'Hello World');
  emit('changeValue');

  function doIt() {
    console.log(`Hello ${name}`);
  }
</script>

License Information:

The copyright of the original repo licensed under the copyright of vuejs and the changes under the copyright of DB Fernverkehr. All code and modifications are licensed under the MIT license.