frctl / fractal

A tool to help you build and document website component libraries and design systems.
https://fractal.build
MIT License
2.11k stars 170 forks source link

Specify target components to Builder #169

Closed mihkeleidast closed 6 years ago

mihkeleidast commented 7 years ago

Hey, I'm running a pretty large pattern library on Fractal, with a total build time approaching 30 minutes. Most of the time, though, I don't really need to build the whole library, but only a single component for a quick prototype demo.

Is it currently possible through the API to specify which components to include in the static build?

allmarkedup commented 7 years ago

@risker woah that is a crazy long build time! How many components do you have in there? I've never had build times anywhere near that long, although I am well aware that the current parse/build system is in serious need of some performance improvements.

Until then... there is no way to only 'build' parts of the UI itself. But if you don't need the UI and just want a rendered component for prototyping then you can certainly use the API to either write a Fractal custom command or a Gulp task or similar to render one or more components and save them to disk.

For example, if you put the following into your fractal.js file:

const fs = require('fs');
const path = require('path');

function renderComponent(args, done){
    const app = this.fractal;
    const target = app.components.find(args.component);
    if (target) {
        app.components.render(target, null, null, {
            preview: args.options.layout
        }).then(function(html){
            const filePath = path.join('./', args.options.output || '', `${target.handle}.html`);
            fs.writeFile(filePath, html, function(err){
                if (err) {
                    app.cli.console.error(`Error rendering ${args.component} - ${err.message}`);
                } else {
                    app.cli.console.success(`Component ${args.component} rendered to ${filePath}`);
                }
                done();
            });
        });
    } else {
        app.cli.console.error(`Component ${args.component} not found`);
    }
};

fractal.cli.command('render <component>', renderComponent,  {
    description: 'Render a component',
    options: [
        ['-l, --layout', 'Render the component within it\'s preview layout.'],
        ['-o, --output <output-dir>', 'The directory to render the component into, relative to the CWD.'],
    ]
});

You could then use the command fractal render @foo from within your project and it would render the @foo component and save it as foo.html, or if you want to render it with within it's preview layout you could run fractal render @foo --layout. There is also an option for specifying the relative output directory (i.e. fractal render @foo --output exported/components.

Would something like that work for you?

allmarkedup commented 7 years ago

@risker and If you want to run through all (or some of) your components and renderer them without the web UI you may also want to check out this issue for some ideas: https://github.com/frctl/fractal/issues/140#issuecomment-254654538

mihkeleidast commented 7 years ago

@allmarkedup thanks for looking into this!

Starting a build tells me it's trying to export 3164 items. Is that a lot? :) (we treat full page prototypes as components as well, and we got a lot of different page templates)

Our main goal with this idea/request would still require the web UI. I'll try to explain this more: rather often somebody on our team is asked to build a prototype page based on our pattern library. When it comes to handing off that prototype to another team to implement, we currently have the following options:

So some kind of filtering system for the builder would come in handy, based on (an array of) component handles. Or even tags! Thinking "build a pattern library of components and dependencies that have "registration-flow" tag attached"...

allmarkedup commented 7 years ago

@risker Ok I understand a bit better now, but unfortunately that is not something that is supported right now. A filtering system would be handy but Fractal's dependency tracking is not super-robust at the moment so would need to be improved before something like this could be implemented.

I think possibly the best thing I can do for you in the near future is to improve the speed of the build process which I'll hopefully get to look at soon!

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

thomasaull commented 3 years ago

Hey @allmarkedup, I have a follow up question regarding your code example: https://github.com/frctl/fractal/issues/169#issuecomment-256166317

In my project, for a full build, I'm setting a static path like in the documentation: https://fractal.build/guide/web/configuration-reference.html#static-path, so that in components this path:

/images/my-image.jpg

when doing a build, becomes this:

../../images/my-image.jpg

However, I can't get this behaviour working when using the components.render() function. Probably because I don't know how to configure the static path in this case. I searched the documentation, source codea and the Discord but I can't find anything which does help me in this case. Do you have any pointers for me?

mihkeleidast commented 3 years ago

@thomasaull you need to specify env.request.path to the render method:

const env = {
    request: {
        path: page.meta.path + 'index.html',
    },
};
component.render(context, env).then((html) => {
    console.log(html)
});
thomasaull commented 3 years ago

@mihkeleidast Appreciate your help and it seems like this is the bit I was missing. One (probably) last question: When I'm doing a full build, the _env.request.path property is something like /components/preview/my-component

Everything works, when I'm hard coding this string in my function like in your example. But there is probably a smarter way to do this. This might be what your page.meta.path is doing — is the page variable in your case the result of fractal.components.find()?

mihkeleidast commented 3 years ago

Yeah we have a static path defined in the component (page) meta and we render all the pages this way.

thomasaull commented 3 years ago

@mihkeleidast Thanks for the explanation and for your help! :)