TheComputerM / svelte-materialify

A Material UI Design Component library for Svelte heavily inspired by vuetify.
https://svelte-materialify.vercel.app
MIT License
622 stars 84 forks source link

New api generator discussion #26

Open Graphmaxer opened 3 years ago

Graphmaxer commented 3 years ago

Currently we are using sveltedoc-parser to generate JSON files that we can use in the website as API documentation.

Svelte is moving to Typescript and components too. Multiple options are available :

I started to implement a mix between sveltedoc-parser and typescript compiler api in this PR : #25.

Feel free to discuss about it.

TheComputerM commented 3 years ago

Yeah, and I also have been meaning to revamp the API table style, any suggestions/screenshots are welcome.

soft-decay commented 3 years ago

@Graphmaxer You've mentioned keeping the documentation in the .svelte files using sveltedoc-parser, but it seems to me the best solution in the long run would be to migrate the components to typescript (<script lang='ts'>) and use typedoc on them, probably by running it on the content of the <script> tag (extracted using the svelte parser).

I tested the project with typescript components and both build and test are passing. The only part I'm not too sure about yet is how to correctly expose the types for library usage. Importing from svelte-materialify/src or svelte-materialify/src/components/<ComponentName> correctly shows the typings from the .svelte files, but importing from svelte-materialify still points to svelte-materialify/@types. Would it be possible to generate d.ts declaration files from the .svelte to avoid having to maintain the type files?

I'd like to work on this if you have not already started something. I feel using this approach would be better because it provides both typing and documentation directly in the component file, thus reducing file juggling and documentation duplication.

Graphmaxer commented 3 years ago

I agree with you @soft-decay, typescript is probably the good way to document components. I don't know any solution about the library usage, i think the svelte team is also figuring out the best way to distribute libraries with typings (RFC). And feel to free to experiment and try, i'm not working on this currently.

TheComputerM commented 3 years ago

Ok then, we have two approches: 1) Use lang=ts. 2) Generate .d.ts files using sveltedoc-parser

Using the 1 method, this will be much better to maintain but we will need to also install typescript when doing an advanced install and I fear the development times would be slower (please correct me if I am wrong). Using the 2 method would be a lot easier but would not provide good definitions. I am personally inclined to the 1 method (we can also provide a custom preprocessor instead of using svelte-preprocess to speed things up?).

Please list anything that I may have missed.

soft-decay commented 3 years ago

@TheComputerM TL; DR I wanted 1 but I think 2 would work well in the meantime.

I've encountered a few problems while experimenting.

  1. sveltedoc-parser does not support typescript inside script tags (this feature), which makes the parsing process rather convoluted when using <script lang="ts"> (i.e. You need a different parser for the script block, then transform it and pass it to sveltedoc-parser, then merge the information from the two parsers and produce a new json file).
  2. Generating declaration files from the content of <script lang="ts"> produces incomplete/incorrect definitions (e.g. No interface, non-optional props, etc).

Because of that, I think it would be best to :

Of course I do not know the full extend of what types you would like to provide, but I think jsdoc could be enough. It would be possible to update the @type {<type>} annotations to : <type> when typescript becomes available in sveltedoc-parser.

soft-decay commented 3 years ago

So I worked on a declaration (d.ts) generator and I have a first working draft. I would like some input on how to improve it. Here's how it plugs into the api script:

  1. Generate a document with sveltedoc-parser using generateJSON (which now also returns the produced document)
  2. Pass the generated document to the typings generation function (generateTypings)
  3. The document is sent to a compiled handlebars template which does the following:
    1. Add basic hard-coded imports (right now { SvelteComponent } from './shared')
    2. Add known imports from known types present in the document. This is less than ideal, but it is necessary to import unknown types like RippleOptions)
    3. Populate an interface with the visibility: 'public' (i.e. exported) props:
      1. Add documentation if necessary (needs at least a description, default value, or non-type keywords)
      2. Add type annotation (?: <type>)
      3. Export a declared class with the props interface (like in the current declaration files)
    4. Returns a string
  4. The resulting declaration string is saved in a file in ./@types/<ComponentName>.d.ts

A small number of manually created declarations would have to be to kept in a separate folder for step 2.2. I spotted these:

You can check the changes I made in PR #76.

TheComputerM commented 3 years ago

I feel like storing all the components in a single .d.ts file is better. Also everything has to be re-hauled as there is a better way to write definitions now.

Graphmaxer commented 3 years ago

Nice work here @soft-decay, I think that the Svelte team is changing to SvelteComponentTyped instead of SvelteComponent recently. They are also talking about Svelte 4.0 which probably come with SvelteKit in 2021 and also with typed components. So maybe your work should be used right now because the current api generator is not working. And in the future we should switch to official typed components with Svelte 4.0.

soft-decay commented 3 years ago

For sure I was not expecting this to be a permanent solution, just a way to reduce maintenance/effort on the typings in the short term. The new SvelteComponentTyped is a really good thing, but I don't think that it resolves the issue of the api-generator, it just changes the format of the declaration files. The declarations could be generated using the SvelteComponentTyped declaration and definitely could all be bundled in a single declaration file. It just means adjusting the generation template.

Should I go for it (same steps described above but with SvelteComponentTyped and all in a single file)? A single file would also remove the need for step 2.ii, which is a nice plus.

Graphmaxer commented 3 years ago

For the single file, go for it and for the usage of SvelteComponentTyped, i don't know if we should switch to this, it is very new and i don't know if the language tools (extension vor vscode) is working properly with it. We should maybe wait and switch to SvelteComponentTyped in a second step.

soft-decay commented 3 years ago

From what I tested, SvelteComponentTyped works out of the box in vscode. It seems like the only requirement is to update dependencies to "svelte": "^3.31.0" (both dev and peer).

The bundled declarations file is over 2000 lines, but here is a snippet:

import { SvelteComponentTyped } from 'svelte'
import { TransitionConfig, blur, crossfade, draw, fade, fly, scale, slide } from 'svelte/transition'
import { RippleOptions } from './@types/Ripple'

interface AlertProps {

  class?: string;

  /**
   * @default true
   */
  visible?: boolean;

  transition?: any;

  transitionOpts?: any;

  /**
   * @default false
   */
  dense?: boolean;

  /**
   * @default false
   */
  outlined?: boolean;

  /**
   * @default false
   */
  text?: boolean;

  /**
   * @default false
   */
  tile?: boolean;

  /**
   * @default false
   */
  dismissible?: boolean;

  /**
   * @default false
   */
  border?: boolean;

  /**
   * @default false
   */
  coloredBorder?: boolean;

}

export declare class Alert extends SvelteComponentTyped<AlertProps> {}

There is still a lot to be done for the component typings and documentation. I Also included a list of known imports at the start of the bundle.

What else would make it better and easier to use? I'm thinking adding events and slots.

Right now index.d.ts can be generated with yarn api.

Graphmaxer commented 3 years ago

Looks good to me ! Nice work @soft-decay ! 👍

We can also use the default attribute of SvelteComponentTyped for default value of props.

TheComputerM commented 3 years ago

Also we have to parse events, slots and even sass variables (know any good way to do that?).

soft-decay commented 3 years ago
TheComputerM commented 3 years ago

This is better for sass https://github.com/mleg/sass-vars-to-js-example, @soft-decay we can customize the variables in svelte materiaify so it is needed to show what variables can be customized.

Graphmaxer commented 3 years ago

@soft-decay You can see it here, should with default event/prop/slot.

soft-decay commented 3 years ago

@Graphmaxer Are you referring to the 3rd generic param? That would be for slots, and the default keyword here refers to the default slot. You can also see it in the declaration. If that is not it, I'm sorry I could not see what you meant.

@TheComputerM typed-scss-modules seems pretty good. I'll try it and see if it works well enough for the project's setup.

Graphmaxer commented 3 years ago

@soft-decay Yes exactly, in this RFC, they also say :

This would not be a breaking change to the existing definition because of the any type by default for props/events/slots.

this default parameter should work with props/events/slots (need testing to be sure).

soft-decay commented 3 years ago

@Graphmaxer I see what you're saying, but my understanding is that it refers to the default slot, that is, the unnamed slot. For example :

import { SvelteComponentTyped } from 'svelte';

export class MyComponent extends SvelteComponentTyped<{}, {}, { default: { numberProp: number } }> {}

lets you do this (because the unnamed slot is the default slot) :

<script lang="ts">
  import { MyComponent } from "myModule";
</script>

<MyComponent let:numberProp>{{ numberProp }}</MyComponent>

But this :

import { SvelteComponentTyped } from 'svelte';

export class MyComponent extends SvelteComponentTyped<{}, {}, { content: { numberProp: number } }> {}

Let you do this :

<script lang="ts">
  import { MyComponent } from "myModule";
</script>

<MyComponent>
  <div slot="content" let:numberProp>{{ numberProp }}</div>
</MyComponent>

In both case they would be typed as a number. Let me know if I missed something, but I did not see a way to set default values directly on the type (a example syntax would help understand). Right now they are set throught the @default jsdoc annotations.

Graphmaxer commented 3 years ago

@soft-decay You're right. I misunderstood this default attribute ^^.

alexprey commented 3 years ago

Hi, I'm plan to start implement the TS support in sveltedoc-parser (https://github.com/alexprey/sveltedoc-parser/issues/34), but actually I dont use svelte with TS and it will be very nice if you have few examples with typescript svelte components