sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.78k stars 4.23k forks source link

Neater approach to code generation #652

Closed Rich-Harris closed 7 years ago

Rich-Harris commented 7 years ago

The code that generates code is really quite tricky to follow. For the sake of discussion, I'll choose perhaps the easiest example:

if (generator.css && options.css !== false) {
  builders.main.addBlock(deindent`
    function ${generator.alias('add_css')} () {
      var style = ${generator.helper('createElement')}( 'style' );
      style.id = ${JSON.stringify(generator.cssId + '-style')};
      style.textContent = ${JSON.stringify(generator.css)};
      ${generator.helper('appendNode')}( style, document.head );
    }
  `);
}

There's a lot of noise there. I'm wondering about a different approach, whereby snippets are written in more natural JavaScript...

function add_css () {
  var style = createElement('style');
  style.id = __id__;
  style.textContent = __css__;
  appendNode(style, document.head);
}

...and turned into data that can be manipulated like so:

if (generator.css && options.css !== false) {
  builders.main.addBlock(addCss({
    id: JSON.stringify(generator.cssId + '-style'),
    css: JSON.stringify(generator.css)
  }));
}

Helpers can be automatically identified and aliased, and the snippets — being valid JS — can be run through Prettier etc.

We could use comment syntax as well:

function create_main_fragment (state, component) {
  /** #if variables */
  var __variables__;
  /** /if */

  return {
    /** #if key */
    key: __key__,
    /** /if */

    /** #if create */
    create: function () {
      /** #block create */
    },
    /** else */
    create: noop,
    /** /if */
  };
}

(That might need a bit more work, admittedly.)

We're already doing something vaguely along these lines with the shared helpers (figuring out which helpers depend on which other ones, etc), so I reckon this is worth exploring.

kzc commented 7 years ago

At what point do shared helpers become a library?

Rich-Harris commented 7 years ago

My take: at the point where you as a developer have to consider them separately (including worrying about which helpers are included) from your app code — in other words, never. In most cases, we're talking about things like this...

export function detachNode(node) {
  node.parentNode.removeChild(node);
}

...and I'd argue it doesn't really make sense to call that 'library code' anyway. Depends on your semantics though!

Rich-Harris commented 7 years ago

Closing this in favour of #673