Skip to content

Improve integration interface#1

Open
mechanoid wants to merge 24 commits into
complate:masterfrom
mechanoid:improve-integration-interface
Open

Improve integration interface#1
mechanoid wants to merge 24 commits into
complate:masterfrom
mechanoid:improve-integration-interface

Conversation

@mechanoid

Copy link
Copy Markdown
  • ships new complate-express interface (app.use(complate()) and res.render('my-component')
  • adds example test application
  • adds npm5 lock file
  • remove complate-jsx dependency from core project

Falk Hoppe added 6 commits July 23, 2017 12:04
complate-jsx is only needed as build dependency. Will add it to an example test project.
given a bundled set of complate components, exporting the complate documentRenderer

```
const bundle = require(path.resolve(__dirname, "dist", "bundle.js"));
```

we can hand this bundle to complate-express and register it as middleware

```
app.use(complate(bundle));
```

Then we should be able to render arbitrary components with 

```
app.get("/", (req, res) => {
	return res.render("site-index", { title: "Hello World!" });
});
```
there is only a minimal manual test given

```
cd example
npm install
npm test
curl http://localhost:5000
~ Hello World!
```
this was inherited by complate-jsx before

@FND FND left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! I have a few notes - so to avoid getting bogged down by largely unrelated issues, I'd suggest we focus on getting the actual integration enhancement (i.e. #render) merged ASAP and discuss the remaining stuff elsewhere; WDYT?

Comment thread .gitignore
@@ -1,2 +1,3 @@
/node_modules
/.eslintcache
/example/dist

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and yet you included example/dist/bundle.js - I assume that's unintentional?

Ah, I just noticed ba2c92c removes that file again - I wouldn't want to see it in the history at all though. (Don't worry about it for now; I'll probably rewrite some history anyway before merging this.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly, and ok, i'm not that fan of rewriting the hist, but sure.

Comment thread example/index.js
return res.render("site-index", { title: "Hello World!" });
});

app.listen(5000);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While generally I'm a great advocate of providing minimal samples, in this case I'm wondering whether complate-sample-express isn't pretty much the same thing? This one feels a little like polluting the repo.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing I dislike on that complate-express-example is that it is not attached to the actual code, and it does not necessarily evolve with the given code. Providing it in the example section and even further making it a test artifact ensures that it stays in synch.

Comment thread example/views/default-layout.jsx Outdated
</body>
</html>;
});
export default { hello: true };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to be used anywhere?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug output, I will remove it

Comment thread example/views/index.jsx Outdated
@@ -0,0 +1,11 @@
// eslint-disable-next-line no-unused-vars

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary if ESLint knows about the JSX pragma.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. In my editor i got warnings about that

@FND FND Jul 24, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need this: https://github.com/complate/complate-jsx/blob/da474a45c2877fd365fb5593b957105bf80fcce1/samples/.eslintrc#L8

🤔 Perhaps we should package parts of that ESLint config so users just need to reference that:

extends:
- fnd # optional; might as well be semistandard
- complate

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the necessary options to the example project, but packaging sounds like a good thing. But it may be rather a thing like complate-jsx, right?

Comment thread src/index.js
"use strict";

let WritableStream = require("./writable_stream");
const WritableStream = require("./writable_stream");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still seems odd to me, as the community in my opinion strives for the latter. And especially while I don't want such variables to be reassigned later on let seems not right to me. In my opinion let gives a very clear signal, that I want to change the value after the first assignment. Using it by default removes that signal-function. But maybe its another discussion.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm with @mechanoid.

@FND FND Jul 24, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's cute how y'all think this is a democracy, or that rational arguments might sway me.

Seriously though, let's just adhere to the surrounding code here and discuss stylistic concerns elsewhere (or, you know, not at all).

Comment thread src/index.js Outdated
response.end();
}
return (req, res, next) => {
res.render = (tag, params) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach (seems much simpler than Marko's solution), but I'm wary of overriding #render; I'd rather we add a method of our own instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried to go with the standard mechanism of app.set('view engine') to make it work as designed, but then the first param has to be a filePath, that actually has to exist. So adding it via middleware-override is already a hack, so yes you may be right.

res.complate('some-component') should make it more clear?!

Comment thread src/index.js Outdated
this.reload();
}
module.exports = bundle => {
const _render = bundle;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This alias seems redundant and indeed unnecessary?

Also: No. (This applies pretty much anywhere const is being used.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I derived that from the code parts available before. Maybe bundle is not that good as param name so I will rename it

Falk Hoppe added 8 commits July 23, 2017 20:32
It also removes the redundant variable assignment
this allows a test setup (once it is provided) to test agains the latest stable libraries
this file should not be checked in, while it does
not help to make this package more resilient.
Tests (also manual smoke tests) should always run
agains the latest stable dependencies
just rely on complate-jsx to handle the build pipeline aspects
add missing target to bundle build
This allows the complate rendering process to be up-2-date with
re-created renderer-bundles once they changed.
Comment thread src/index.js Outdated
module.exports = rendererPath => {
return (req, res, next) => {
const renderer = typeof rendererPath === "function" ?
rendererPath :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:trollface:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming and life is hard ;)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to \t:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣 sorry, my bad twin was at work, I guess. But funny, that eslint isn't even mentioning it

Falk Hoppe added 3 commits July 24, 2017 15:24
Comment thread README.md Outdated

## Quick Start Guide

Install complate-express to your express application

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Install to/Install in/?

Comment thread README.md
Install complate-express to your express application

```
npm install complate-express

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--save?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's implicit since npm v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whut? Didn't know that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the crucial part of npm5 ;)

Comment thread example/package.json
"express": "^4.15.3"
},
"devDependencies": {
"complate-jsx": "^0.7.1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a devDependency?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

complate-jsx is essentially a meta-package which abstracts the tools required for bundling and transpiling JSX modules (i.e. faucet-pipeline-js and babel-plugin-transform-react-jsx) - since complate templates are precompiled, those tools are not required at runtime

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this definetly is a thing to be discussed, because often the same CI environment which runs the bundle process, is already running in production mode. But this is not important for that example application. And yes this dependency is not necessary once the bundle is created, so it should not be delivered to production surroundings.

@FND FND Jul 24, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I've been wondering about that CI issue as well ¯\_(ツ)_/¯

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a problem of this package — especially because it is just for the example part — but I recommend a layered build, in which dev-dependencies are installed, used and removed before released.

Comment thread src/index.js
}

return require(rendererPath);
}

@FND FND Jul 24, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled to grok what's really going on in this module. How about this:

module.exports = bundlePath => {
    bundlePath = path.resolve(bundlePath);
    let render = require(bundlePath);

    return (req, res, next) => {
        if(!res.app.enabled("view cache")) { // reload
            delete require.cache[bundlePath];
            render = require(bundlePath);
        }

        res.complate = (tag, params) => {
            let stream = new WritableStream(res);
            render(stream, tag, params);
            res.end();
        };

        next();
    };
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, while that is more readable yes, it looses some options I think a worth the hassle.

  1. it does not allow to inject the bundle directly anymore, which decreases the possibilities of the user and decreases testing capabilities IMHO

  2. cache configuration is missing. While I see the point, that the require.cache override is useful, it is quire intrusive, and it may be a good thing (at least for debugging purpose) to be able to skip it.

  3. it requires the file twice if the cache is disabled

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I still haven't grokked it then:

  1. I'm not sure that flexibility (and thus complexity) is warranted; YAGNI, also from a testing perspective

    however, seems like an easy fix:

    if(bundlePath.call) {
        render = bundlePath;
    } else {
        bundlePath = path.resolve(bundlePath);
        render = require(bundlePath);
    }
  2. in your code, AFAICT, you're always passing { cache: true } into resolveRenderer, which is a private function - so I don't understand what the intent there is

  3. well spotted, that's a bug then - but again, should be easy to fix?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. maybe you are right there, will remove that part, to keep the interface easier, especially because of the behavioral change for function or path injection in terms of caching.

  2. removed the options param from the interface by accident. Will add it again. I think a little bit control for that part seems still legit

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a side note. I would stay by 'rendererPath' instead of 'bundlePath' while that is the crucial part about that content, right? 'bundlePath' gives not really a hint about what the bundle is expected to contain (while that might be obvious)

@FND FND Jul 31, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to be obtuse, but I still don't understand the purpose of that cache parameter: Why not just rely on Express's "view cache" setting? So it seems to me this should suffice:

"use strict";

let WritableStream = require("./writable_stream");

// middleware to extend response object with `#complate` method
module.exports = bundlePath => {
    bundlePath = path.resolve(bundlePath);

    return (req, res, next) => {
        if(!res.app.enabled("view cache")) { // ensure bundle is reloaded
            delete require.cache[bundlePath];
        }
        let render = require(bundlePath);

        res.complate = (tag, params) => {
            let stream = new WritableStream(res);
            render(stream, tag, params);
            res.end();
        };

        next();
    };
};

As for rendererPath vs bundlePath, I'd like to stick to the latter: It's not only an established convention elsewhere, but it actually seems more descriptive of what we're after there. Yes, the reader needs to be aware that this is where the render method comes from - but that's the entire point of this exercise, so it doesn't seem like an onerous expectation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe you are right about that, an additional config may be not necessary, right.

For the bundlePath I personally still don't like that, because it actually says nothing about its content or about what it has to expose. But I don't really care about that very much.

Will update that PR accordingly

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the PR accordingly

@FND FND Jul 31, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've merged this part as 7c845c6 (released as v0.9.0) because it moves things forward without getting bogged down by the remaining changes.

This commit also includes some adjustments WRT commit message, variable naming and conciseness (IMO anyway), plus it ensures bundlePath is absolute (I think? it's late and I'm tired... ).

Thank you for your patience; I'm somewhat overstretched at the moment.

Comment thread README.md Outdated

## API

#### complate(rendererPath: String|Renderer, options: Object)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might want to mention that we're not using Express's view-engine mechanism because it doesn't support streaming - cf. http://markojs.com/docs/express/#skip-the-view-engine

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely true, but maybe lets improve the README file afterwards?!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, fine by me - I just figured I'd mention it

Falk Hoppe added 7 commits July 24, 2017 19:13
removed the possibility to inject a required module in favor of keeping the interface simple. If it is necessary or useful in the future it can be extended easily
@FND

FND commented Nov 22, 2017

Copy link
Copy Markdown
Contributor

Sorry for leaving this unattended for so long. (July? WTF!)

Some of this has since been implemented (7c845c6 ff.), leaving the README and example application.

I'll see whether I can cherry-pick and update the former soon - no promises though, I'm a little short on cycles lately.

As mentioned elsewhere, I'd prefer to keep the latter in a separate repo - though I realize that comes with its own drawbacks, so let's discuss that when we get a chance.

FND added a commit that referenced this pull request Nov 23, 2017
based on @mechanoid's efforts:
#1
FND added a commit that referenced this pull request Nov 23, 2017
based on @mechanoid's efforts:
#1
FND added a commit that referenced this pull request Nov 23, 2017
based on @mechanoid's efforts:
#1
@FND FND mentioned this pull request Nov 23, 2017
FND added a commit that referenced this pull request Nov 23, 2017
based on @mechanoid's efforts:
#1
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

Successfully merging this pull request may close these issues.

3 participants