quasar
quasar copied to clipboard
Support JSX/TSX
quasar create quasar-tsx --branch next
module.exports = { presets: [ '@quasar/babel-preset-app', ], plugins: ['@vue/babel-plugin-jsx'] }
https://github.com/vuejs/jsx-next
tsx is not supported in quasar yet unfortunately afaik I guess there are more prio issues to be done currently Seems like the components go through something else first before coming to the jsx transpiler and it breaks
Would be nice if this gets supported at some time because it has much cleaner syntax than render functions
@panhezeng Im using TSX in "3.0.0-beta.10" Quasar/App, your babel.config.js is correct, but you need config "babel loader" on "quasar.conf.js";
see my config on "extendWebpack"
extendWebpack (cfg, { isServer, isClient }) {
cfg.resolve.alias = {
...cfg.resolve.alias,
vue$: 'vue/dist/vue.esm-bundler.js',
'@': path.resolve(__dirname, './')
}
cfg.resolve.extensions.push('.tsx')
cfg.module.rules.push({
test: /\.ts(x?)$/,
exclude: /(node_modules|quasar)/,
use: [
{
loader: 'babel-loader',
options: { babelrc: true }
},
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
}
}
]
})
cfg.module.rules.push({
test: /\.tsx$/,
exclude: /(node_modules|quasar)/,
use: [
{
loader: 'babel-loader'
},
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
}
}
]
})
}
},
in addition to this configuration, you need to define the suppertTS
supportTS: {
tsCheckerConfig: {
eslint: {
enabled: false,
files: './src/**/*.vue'
}
}
},
@joaomede Is q-dialog working for you in tsx? For me it is broken and always shown in the component as a visible block element
Actually I get for all quasar components a warning that it cannot resolve when used in tsx file
@joaomede did you manage to make JSX/TSX work with Quasar? Do you have some time to contribute with a PR adding it out of the box?
@IlCallo Yes man, I Will Do
Thanks :) Reach me out on Discord if you need any assistance
Hello Guys, @IlCallo, @Smrtnyk, @panhezeng. I published a working example of "Quasar v2 / Vue 3" with official plugin "https://github.com/vuejs/jsx-next".
https://github.com/joaomede/Quasar-Vue3-TSX https://codesandbox.io/s/github/joaomede/Quasar-Vue3-TSX
Comments:
- quasar v2 components are not returning JSX/TSX types, to solve this the "any" type needs to be pegged after import, eg:
import { defineComponent } from 'vue'
import { QBtn as Btn } from 'quasar'
const Component = defineComponent({
setup() {
const QBtn = Btn as any
return () => <QBtn label={'label here'}>
}
})
you can add this to quasar.conf
sourceFiles: {
rootComponent: "src/App",
},
and rename your App.vue
to App.tsx
I don't have the type issues with quasar components when I use kebab-case naming for them for some reason
@Smrtnyk, updated my example https://codesandbox.io/s/github/joaomede/Quasar-Vue3-TSX, changing the file "app.vue" to "app.tsx", thanks for that.
about this:
I don't have the type issues with quasar components when I use kebab-case naming for them for some reason
- The configuration expects this type:
- however, quasar components return this type:
Therefore, the error will always appear; when you use kebeb-case, the configuration assumes that you are using regular html, since in the quasar configuration you defined the components.
@joaomede how can I return interfaces which are both TSX compatible AND retain existing behaviour (to describe the component public API)?
@IlCallo, I believe that's the problem here, the project generates types, overlapping the originals, https://github.com/quasarframework/quasar/blob/918af69abb4e855f0848cd699b0413fed65e51e2/ui/build/build.types.js#L311 I believe this needs to be revised as the original types returned by the components work correctly.
the exports types is all correct, the problem is on replaced types, in https://github.com/quasarframework/quasar/blob/918af69abb4e855f0848cd699b0413fed65e51e2/ui/build/build.types.js#L311
Interesting... Since the naming convention holds with interface and file name, I should be able to just import the type from the file itself for all components, will check what I can do with it
@IlCallo, I generated an type expected by JSX, including the JSDoc comments from the props are working.
import { LooseDictionary, QBtn as Button } from 'quasar'
import { defineComponent, DefineComponent, ComponentOptionsMixin, EmitsOptions, AllowedComponentProps, VNodeProps, ComponentCustomProps } from 'vue'
export type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
type NewType = DefineComponent<{}, () => JSX.Element, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, PublicProps, Readonly<{} & {}>, {
/**
* Size in CSS units, including unit name or standard size name (xs|sm|md|lg|xl)
*/
size? : string
/**
* Define the button HTML DOM type
*/
type? : 'a' | 'submit' | 'button' | 'reset'
/**
* Equivalent to Vue Router <router-link> 'to' property
*/
to? : string | LooseDictionary
/**
* Equivalent to Vue Router <router-link> 'replace' property
*/
replace? : boolean
/**
* Equivalent to Vue Router <router-link> 'append' property
*/
append? : boolean
/**
* The text that will be shown on the button
*/
label? : string | number
/**
* Icon name following Quasar convention; Make sure you have the icon library installed unless you are using 'img:' prefix
*/
icon? : string
/**
* Icon name following Quasar convention; Make sure you have the icon library installed unless you are using 'img:' prefix
*/
iconRight? : string
/**
* Use 'outline' design
*/
outline? : boolean
/**
* Use 'flat' design
*/
flat? : boolean
/**
* Remove shadow
*/
unelevated? : boolean
/**
* Applies a more prominent border-radius for a squared shape button
*/
rounded? : boolean
/**
* Use 'push' design
*/
push? : boolean
/**
* Applies a glossy effect
*/
glossy? : boolean
/**
* Makes button size and shape to fit a Floating Action Button
*/
fab? : boolean
/**
* Makes button size and shape to fit a small Floating Action Button
*/
fabMini? : boolean
/**
* Apply custom padding (vertical [horizontal]); Size in CSS units, including unit name or standard size name (none|xs|sm|md|lg|xl); Also removes the min width and height when set
*/
padding? : string
/**
* Color name for component from the Quasar Color Palette
*/
color? : string
/**
* Overrides text color (if needed); Color name from the Quasar Color Palette
*/
textColor? : string
/**
* Avoid turning label text into caps (which happens by default)
*/
noCaps? : boolean
/**
* Avoid label text wrapping
*/
noWrap? : boolean
/**
* Dense mode; occupies less space
*/
dense? : boolean
/**
* Configure material ripple (disable it by setting it to 'false' or supply a config object)
*/
ripple? : boolean | LooseDictionary
/**
* Tabindex HTML attribute value
*/
tabindex? : number | string
/**
* Label or content alignment
*/
align? : 'left' | 'right' | 'center' | 'around' | 'between' | 'evenly'
/**
* Stack icon and label vertically instead of on same line (like it is by default)
*/
stack? : boolean
/**
* When used on flexbox parent, button will stretch to parent's height
*/
stretch? : boolean
/**
* Put button into loading state (displays a QSpinner -- can be overridden by using a 'loading' slot)
*/
loading? : boolean
/**
* Put component in disabled mode
*/
disable? : boolean
/**
* Makes a circle shaped button
*/
round? : boolean
/**
* Percentage (0.0 < x < 100.0); To be used along 'loading' prop; Display a progress bar on the background
*/
percentage? : number
/**
* Progress bar on the background should have dark color; To be used along with 'percentage' and 'loading' props
*/
darkPercentage? : boolean
/**
* Emulate click on QBtn
* @param evt JS event object
*/
click (evt? : LooseDictionary): void
}>
const QBtn = Button as any as NewType
export default defineComponent({
setup () {
return () => (
<QBtn label={'Working!!!'}></QBtn>
)
}
})
What's Readonly<{} & {}>
for?
I believe they need to receive similar values as props, however, it will serve for "single-file.vue" intellisense, but this needs to be validated better.
original declare from vue https://github.com/vuejs/vue-next/blob/f258f5d2c2774eb2a2c2e4a0ac235b23d0cf9968/packages/runtime-core/src/apiDefineComponent.ts
export type PublicProps = VNodeProps &
AllowedComponentProps &
ComponentCustomProps
export type DefineComponent<
PropsOrPropOptions = {},
RawBindings = {},
D = {},
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions,
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string,
PP = PublicProps,
Props = Readonly<ExtractPropTypes<PropsOrPropOptions>> & EmitsToProps<E>,
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
> = ComponentPublicInstanceConstructor<
CreateComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
PP & Props,
Defaults,
true
> &
Props
> &
ComponentOptionsBase<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
Defaults
> &
PP
Meanwhile, I found a way to infer the DefineComponent
type from the JS file and generate it as a TS definition, but since we're using render functions seems like there's no way to automatically infer the public API defined via expose()
.
Also note there's this change pending in dev branch: https://github.com/quasarframework/quasar/commit/918af69abb4e855f0848cd699b0413fed65e51e2
Can you try if patching ComponentConstructor
helper type in some way can help us get TSX support?
I performed that changeto get full inference into Vue Test Utils, but is still unreleased
To test it, you should clone quasar repo locally, enter ui
folder, run yarn install
, then yarn build
, then rm -rf node_modules
(this is important) and finally install the local dependency into your project with yarn add <quasar package folder>/ui
@IlCallo, you branch, https://github.com/quasarframework/quasar/commit/918af69abb4e855f0848cd699b0413fed65e51e2 solved the problem partially, lint stopped complaining, however, the component is only exposing 2 keys, they are; "key" and "ref", the declared props are not found yet.
Yep, that's the same problem I reached right now in today experiments branch here
Apparently there's no way to infer stuff exposed via expose
.
I found a way to extract props from inferred DefineComponent
interface doing Parameters<(typeof QAjaxBar)['setup'][0]>
(QAjaxBar
is the type generated starting from the JS file), so if you only need the props we can expore a bit more on that side.
Consider that we need to fix many problems and add JS annotations to get acceptable inferred types, I only did a small experimento on QAjaxBar today
the declared props are not found yet
That's why I told you to tinker a bit with ComponentConstructor
helper, maybe you can use existing docs-generated interfaces to add something there, even if I guess you'll then lose distinction between props, methods, etc and break VTU extraction in some way 🤔
I got in touch with Vue Core team, an advanced guide to Vue interfaces should be in the works, as well as a refactor of TS classes to make them more easy to use by library authors. They'll export a JSX compatible interface for components too, so let's cross fingers 🤞🏻
any updates? JSX/TSX works with Vite, but types not provide class and style attributes.
Seems like Vue team on this regard stopped due to the major docs rewrite they're doing Pinged them to get some info
Last time I heard them they told me it's possible to use RenderComponent
interface to declare a JSX-compatible component, but that interface doesn't seem exist anymore
I'd definitely be interested in having this supported by default with the CLI, I can get by using the render methods for now but having JSX definitely helps for readability in certain cases.
any updates? JSX/TSX works with Vite, but types not provide class and style attributes.
Does this class and style attributes already solved?
Is there a proper fix for this that retains the types? Trying to use tsx with quasar components, can't define a click handler for QBtn as click event is not a property for some reason. https://github.com/vuejs/babel-plugin-transform-vue-jsx
Allowing JSX/TSX for newly authored components and slowly transition existing components to use it would be huge and make contributing a lot easier. SFCs would help similarly though the switch from render functions to JSX is a lot simpler and makes it easy to adopt.
There have been several instances where I wanted to contribute (mostly small stuff) but was deterred by the need to manually write render functions.
We don't plan to convert Quasar components to JSX/TSX or SFC, this issue is about supporting JSX/TSX into userland We need the flexibility, and thus complexity, of render functions to write components optimized for performance