Giter Club home page Giter Club logo

vue-o2c's Introduction

vue-o2c - Vue Options API to Composition API

Demo / Online Playground

WORK IN PROGRESS -- the following is not done:

  • bunch of stuff still not implemented (working through case by case)
  • publish package correctly for CLI command to work (need to check)
  • data() preamble -- if there is preamble maybe just create refs then use the function to set them
  • handle setup() in options api
  • allow options to configure (eg. no typescript)
  • $el needs to try to rewrite part of template
  • would like to maintain indentation

After running, check for FIXME comments

Composition API does not allow easy access of app.config.globalProperties like options API does. vue-o2c takes care of some basic cases (eg. this.$router assuming vue-router) but for others, you will see comments like the following and you must adjust the code depending on how you provide these systems.

const $primevue = inject("primevue") /* FIXME vue-o2c */

Usage

via CLI

This is not working due to a publishing issue I need to fix...

$ npx vue-o2c /path/to/sfc.vue
...
<transformed sfc code>
...

Programmatically

$ pnpm add -D vue-o2c

Please keep in mind, API is very experimental and likely will change!

import { transformPath, transform, type State } from "vue-o2c"

const s1: State = transformPath("/path/to/sfc.vue")
const s2: State = transform("<script>\nexport default {\n  props: {\n    a: String,\n  }\n}\n</script>")
// s1.transformed and s2.transformed will both contained transformed code

Example

Given the following file:

<template lang="pug">
div
  p Wonderful
</template>

<script>
export default {
  props: {
    greeting: {
      type: String,
      default: "Hello",
    },
  },
  data() {
    // this.initializing = true -- would make data() "complex" (need to improve)
    return {
      name: this.$route.query.name || 'John',
      watchMethod: 0,
      watchObject: {
        key: 1,
      },
    }
  },
  methods: {
    meth() {
      console.log(`${this.greeting} ${this.name} ${this.$el.clientHeight}`)
    },
    keyValue: async (a) => {
      await a
    },
  },
  mounted() {
    this.meth()
    this.keyValue()
    delete this.initializing // should not become `delete initializing` (so use $this)
  },
  watch: {
    ["watchMethod"](v) {
      console.log("watchMethod", v)
    },
    "watchObject.key": {
      deep: true,
      immediate: true,
      async handler(v, ov) {
        console.log("watchObject", v, ov)
      },
    },
  },
}
</script>

<style scoped>
:root {
  background: red;
}
</style>
$ git clone [email protected]:tjk/vue-o2c.git
$ cd vue-o2c
$ pnpm i
$ pnpm exec tsx index.ts ./example.vue

Will output the following:

<template lang="pug">
div(ref="$el")
  p Wonderful
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from "vue"
import { useRoute } from "vue-router"

const props = withDefaults(defineProps<{
  greeting?: string
}>(), {
  greeting: "Hello",
})

const $route = useRoute()
const $this = {}

const $el = ref<HTMLElement | undefined>()
const name = ref($route.query.name || 'John')
const watchMethod = ref(0)
const watchObject = ref({
  key: 1,
})

onMounted(() => {
  meth()
  keyValue()
  delete $this.initializing // should not become `delete initializing` (so use $this)
})

watch(() => watchMethod.value, (v) => {
  console.log("watchMethod", v)
})
watch(() => watchObject.value.key, async (v, ov) => {
  console.log("watchObject", v, ov)
}, {
  deep: true,
  immediate: true,
})

function meth() {
  console.log(`${props.greeting} ${name.value} ${$el.value.clientHeight}`)
}
async function keyValue(a) {
  await a
}
</script>

<style scoped>
:root {
  background: red;
}
</style>

vue-o2c's People

Contributors

simlevesque avatar tjk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

vue-o2c's Issues

Error: prop attribute not handled

I am getting error

> [email protected] start /home/<my-project-path>/vue-o2c
> node index.js

/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:44
  throw new Error(`${msg}${n ? ` @ (${n.startPosition.row + 1}, ${n.startPosition.column + 1})` : ""}`);
  ^

Error: prop attribute not handled: required @ (6, 17)
    at fail (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:44:9)
    at Object.onKeyValue (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:614:19)
    at handleObject (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:1058:53)
    at Object.onKeyValue (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:604:11)
    at handleObject (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:1058:53)
    at handleProps (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:593:3)
    at handleDefaultExportKeyValue (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:954:7)
    at Object.onKeyValue (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:1100:30)
    at handleObject (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:1058:53)
    at handleDefaultExport (/home/<my-project-path>/vue-o2c/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-o2c/dist/index.js:1099:3)
 ELIFECYCLE  Command failed with exit code 1.

trying to transform this vue3 option API component:

<template>
  <div class="offer-count">
    <h3 v-if="!loading">{{ count }}{{ countText }}</h3>
  </div>
</template>

<script>
export default {
  name: 'DetailsOffersCount',
  props: {
    count: {
      type: Number,
      required: true
    },
    loading: {
      type: Boolean,
      required: true
    }
  },
  computed: {
    countText() {
      return ` ${this.count === 1 ? 'Angebot' : 'Angebote'} sortiert nach: `
    }
  }
}
</script>

Looks quite similar, if I use npx vue-o2c /path/to/component.vue:

Error: prop attribute not handled: required @ (6, 17)
    at fail (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:37:9)
    at Object.onKeyValue (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:607:19)
    at handleObject (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:1051:53)
    at Object.onKeyValue (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:597:11)
    at handleObject (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:1051:53)
    at handleProps (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:586:3)
    at handleDefaultExportKeyValue (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:947:7)
    at Object.onKeyValue (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:1093:30)
    at handleObject (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:1051:53)
    at handleDefaultExport (/home/nkuhn/.npm/_npx/9a0efcde2cb26dd4/node_modules/vue-o2c/dist/cli.js:1092:3)

Wrong order of const declarations

if OptionsApi has:

const prop0 = 'someProp0 Value';

export default {
  components: {...},
  data() {
    return {
      someVar: someInit({
        prop1: 'prop1',
        prop2: `${prop0}/path`,
      }),
    };
  },
};

Result of converting is:

const someVar= ref(someInit({
  prop1: 'prop1',
  prop2: `${prop0}/path`,
}));

const prop0= 'someProp0 Value';

So value of prop2 is undefined/path

Handle rewrites in components section

import Button from 'primevue/button';

    components: {
        IPButton: Button
    },

should (ideally) transform to

import { default as IPButton } from 'primevue/button';

but maybe more likely

import Button from 'primevue/button';

const IPButton = Button

repository for the demo?

First of all thanks a lot for making this and for continuing to improve it! I've been using it and it's been very helpful and time saving!

I really like your GUI demo and would like to run it locally so I can easily paste in code and get the conversion output.

Is it possible you could share the repository for the demo you have https://tjk.github.io/vue-o2c/ please?

Many thanks!!

Conversion failing for the Inplace Primvue component

I tried converting the code for the Inplace component from Primevue on the online converter but it apparently didn't like it. For it to work, I had to comment three areas of the code like so:

<template>
    <div v-focustrap :class="containerClass" aria-live="polite">
        <div v-if="!d_active" ref="display" :class="displayClass" :tabindex="$attrs.tabindex || '0'" role="button" @click="open" @keydown.enter="open" v-bind="displayProps">
            <slot name="display"></slot>
        </div>
        <div v-else class="p-inplace-content">
            <slot name="content"></slot>
            <IPButton v-if="closable" :icon="closeIcon" :aria-label="closeAriaLabel" @click="close" v-bind="closeButtonProps" />
        </div>
    </div>
</template>

<script>
import Button from 'primevue/button';
import FocusTrap from 'primevue/focustrap';

export default {
    name: 'Inplace',
    emits: ['open', 'close', 'update:active'],
    props: {
        closable: {
            type: Boolean,
            default: false
        },
        active: {
            type: Boolean,
            default: false
        },
        disabled: {
            type: Boolean,
            default: false
        },
        closeIcon: {
            type: String,
            default: 'pi pi-times'
        },
        displayProps: {
            type: Boolean, // initially null
            default: null
        },
        closeButtonProps: {
            type: Boolean, // initially null
            default: null
        }
    },
    data() {
        return {
            d_active: this.active
        };
    },
    watch: {
        active(newValue) {
            this.d_active = newValue;
        }
    },
    methods: {
        open(event) {
            if (this.disabled) {
                return;
            }

            this.$emit('open', event);
            this.d_active = true;
            this.$emit('update:active', true);
        },
        close(event) {
            this.$emit('close', event);
            this.d_active = false;
            this.$emit('update:active', false);
            setTimeout(() => {
                //this.$refs.display.focus();
            }, 0);
        }
    },
    computed: {
        containerClass() {
            return ['p-inplace p-component', { 'p-inplace-closable': this.closable }];
        },
        displayClass() {
            return ['p-inplace-display', { 'p-disabled': this.disabled }];
        },
        closeAriaLabel() {
            //return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.close : undefined;
        }
    },
    components: {
        IPButton: Button
    },
    //directives: {
    //    focustrap: FocusTrap
    //}
};
</script>

<style>
.p-inplace .p-inplace-display {
    display: inline;
    cursor: pointer;
}

.p-inplace .p-inplace-content {
    display: inline;
}

.p-fluid .p-inplace.p-inplace-closable .p-inplace-content {
    display: flex;
}

.p-fluid .p-inplace.p-inplace-closable .p-inplace-content > .p-inputtext {
    flex: 1 1 auto;
    width: 1%;
}
</style>

So it seems like directives are not supported yet as well as certain this.$ references. Is there any way to work around it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.