Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toward a static Vulcan #3

Open
eric-burel opened this issue Mar 22, 2020 · 2 comments
Open

Toward a static Vulcan #3

eric-burel opened this issue Mar 22, 2020 · 2 comments

Comments

@eric-burel
Copy link
Collaborator

eric-burel commented Mar 22, 2020

Problem

Currently, Vulcan rely massively on the application startup process to dynamically generate features:

  • the graphQL schema
  • the Components registry, with replacement support
  • the routes
  • the graphQL fragments
  • the Mongo collections

This approach was a great innovation when Meteor was just a young framework.

But nowadays, web development framework are moving away from dynamic generation, and prefer richer build step.

  • Server side, this is necessary to reduce the startup time of lambdas in serverless architecture
  • Client side, this is necessary to dump the startup step altogether, in order to fasten rendering
  • It's a game changer for 3rd party tools, that can import built code easily (GraphQL schema static analysis, Storybook...).

Vision

For Vulcan, that means replacing all registerComponent, addRoute, registerFragment and the associated startup calls to populate various objects (the router, Components, Collections...) by build-step patterns whenever possible.

We suppose here that the code is structured in packages. The package syntax will probably be specific to Vulcan, but should rely on Next incoming plugin system.

An export standard

See #9

Relevant patterns to combine static build but allow modifications at the developer level

To be completed, multiple patterns can coexist.

Copy paste (appliable to Mongoose schema, Graphql schema)

Allow user to copy autogenerated files into his code for modification (eg for the graphql schema)

Examples

Copy .vulcan/graphqlSchema.graphql to src/lib/graphqlSchema
Generate routes directly in src/pages

Limit

Then how do you update the auto-generated part? How do you detect that users applied modification to the pages?

Mix (for replaceable Components)

Mixing autogenerated components and user override in a single file at build time?
The issue is with nested components.

See VulcanJS/Vulcan#2549

Example

In Vulcan, we define default components:

// in vulcan code

// some non replaceable component
const Footer = ({children}) => <footer>{children}</footer>

// replaceable components (exported)
export const DatatableFooter = ({children}) => (<Footer>{children}</Footer>)
export const DatatableContent = ({children}) => (<main>{children}<DatatableFooter /></main>)
export const Datatable = ({children}) => (<DatatableFooter>{children}</Footer>

In users code, we define some overrided versions, but only for some components. We keep the default for other components:

// in user override
import { DatatableFooter } from `vulcan/ui`

// some non exported code
const StyledDiv = (props) => <div {...props} style={{backgroundColor: "red"}} />
export const DatatableContent = ({children}) => (<StyledDiv>MY CUSTOM CONTENT {children}<DatatableFooter /></StyledDiv>)

It will be mixed at build time into:

// FINAL BUILT FILE
// We first have code necessary to components
// copy pasted as-is from Vulcan
const Footer = ({children}) => <footer>{children}</footer>
// copy pasted as-is from user override
const StyledDiv = (props) => <div {...props} style={{backgroundColor: "red"}} />

// Then components
// from vulcan
export const DatatableFooter = ({children}) => (<Footer>{children}</Footer>)

// nested component that has been overriden
export const DatatableContent = ({children}) => (<div>MY CUSTOM CONTENT {children}<DatatableFooter /></div>)

// from vulcan again
export const Datatable = ({children}) => (<DatatableFooter>{children}</Footer>

Limit

How do you even code that?
How to handle imports in the user code?
How to correctly type props?
How do you handle all additional code, for example 3rd party package used to implement some component? Eg package imported at the top level for each component? The risk of name clashes? Will we need to define only one file per component in Vulcan, so that we explicit the dependency to some additional code on a per-component basis?

Others?

Per feature

Routes

Build step

This means copying package "pages" into the "pages" folder of the Next application.

User overrides

??

Updates in case of user overrides

??

Collections

Build step

This means translating Vulcan schemas into Mongoose schemas.

User overrides

??

Updates in case of user overrides

??

GraphQL schema

Build step

This means creating a .graphql file.

User overrides

???

User extension

We can probably stitch schemas. Use should be able to enjoy text editor features, so writing directly into .graphql files or gql tags.

Relevant tools:

Query and mutation resolvers

??

Fragments

Build step

This means creating multiple .graphql file.

User overrides

We can expect user to create its own fragment, independant from Vulcan default fragments. So no need for overrides.

Mutations and resolvers

We need both the GraphQL part and the JS implementation. For the GraphQL part the process is the same as for any part of a grapqhl schema.

For the function we can simply export and merge into the global schema.

Components

Here the tricky part is replacement. See this issue

i18n files

They are currently a pain point because they may bloat exports. Relying directly on an i18n JSON file could work.

Open questions:

  • Where to we put the generated code? In a .vulcan folder similar to .next and .meteor?
  • How user can extend this code ?
  • How user can override certain parts of the code ? Eg modifying a generated page, a generated graphql schema ? A component ?
  • How do we update the auto-generated part when the user has applied overrides ? For example in the graphql schema?
  • When static analysis is needed (eg to mix Component overrides), where to apply it? To TypeScript code? To built babel code? It probably needs some AST feature?
  • How to handle build step in the dev process? Rebuild on file change?
  • Easy creation of Storybook addon (eg to add i18n to preview for instance)
@swyxio
Copy link

swyxio commented Mar 25, 2020

interesting! cc @jlengstorf - another jamstackifying react framework

@eric-burel
Copy link
Collaborator Author

eric-burel commented Apr 27, 2020

Strapi seems to be relating a lot on static file creation. Creating a new model will create a bunch of new files, representing the data ( a bit like our schema) but also the CRUD operation and model.

The GraphQL part use a "Shadow CRUD" feature that seems to behave like our schema: dynamically generating the resolvers and mutations, and only statically a schema.graphql file.

See plugins/graphql/services/Resolvers.js in a Strapi appi using graphql for more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants