ionic-team / stencil

A toolchain for building scalable, enterprise-ready component systems on top of TypeScript and Web Component standards. Stencil components can be distributed natively to React, Angular, Vue, and traditional web developers from a single, framework-agnostic codebase.
https://stenciljs.com
Other
12.58k stars 788 forks source link

Support server side rendering in v8js #254

Closed mortenson closed 7 years ago

mortenson commented 7 years ago

I'm submitting a:

Current behavior:

The current server side rendering code relies on a Node environment to function, which makes it difficult to run in other Javascript contexts like PHP's v8js.

Expected behavior:

If Stencil distributed a standalone version of the SSR code without using any Node-specific features (ex: globals like require/process/__dirname, filesystem access), it would be a great start to having support for SSR in other environments.

Related code:

I've set up a repository that Stencil contributors can use to test SSR with v8js, which should hopefully kick-start this development. The repository and instructions to use it can be found here: https://github.com/mortenson/stencil-v8js. Running the example PHP file currently throws an error due to the Node dependencies in the SSR code.

Other information:

Vue.js just added SSR support, specifically tested in v8js. You can see the documentation for this here: https://gist.github.com/yyx990803/9bdff05e5468a60ced06c29c39114c6b#environment-agnostic-ssr

adamdbradley commented 7 years ago

The entire compiler is separated from node, and it passes around a sys object. In the case of running stencil from node, it runs the js file in bin, which creates the node sys object, and passes that to the compiler.

Please take a look at @stencil/core/server and @stencil/core/compiler. Hope that helps, thanks

mortenson commented 7 years ago

@adamdbradley That helps, but I'm not sure if all of Stencil's codebase is just using the public sys methods, which would make writing my own really difficult.

I found an example of this here: https://github.com/ionic-team/stencil/blob/master/src/compiler/util.ts#L56, sys.fs.readFile is used, which assumes that sys.fs is available and that it's an instance of the fs Node package. It looks like many other parts of the codebase also directly access sys.fs: https://github.com/ionic-team/stencil/search?utf8=%E2%9C%93&q=config.sys.fs&type=

As a result of this, even if I implemented a version of sys for v8js, I wouldn't have guarantees that only the public methods defined by that Object will be used by the compiler.

mortenson commented 7 years ago

References to other Node packages are also used in the compiler, i.e sys.path and sys.url. While the concept of having an abstract sys Object is good, the current compiler code is still strongly coupled to having specific Node packages and behavior present. I would essentially have to mock all public methods of multiple Node packages to make this work.

adamdbradley commented 7 years ago

Right, you would provide your own sys object, and inside of your functions they would do their way of file reads, write, path joins, etc. But the compiler only knows to call sys.fs.readFile, but however it got the data it is not involved with.

mortenson commented 7 years ago

The problem I have with jumping into this work is that there's no API contract with the code using sys - I have no awareness of what methods I need to mock in sys.fs, sys.path, etc. The code that uses sys is implicitly dependent on Node, since it expects properties of sys to be specific Node packages. As it stands now I would have to mock entire Node packages in v8js, which would be an enormous undertaking.

Vue.js offers is a single JS file for SSR, which has no dependencies on Node and no per-environment mocking required to use it. React follows a similar pattern in its v8js example: https://github.com/reactjs/react-php-v8js, no mocking of Node packages is required to SSR React components.

adamdbradley commented 7 years ago

The entire sys object and what functions go on it is all within typescript. https://github.com/ionic-team/stencil/blob/2c11c7df6c612ae84ff7d5aa39413617ff6c2351/src/util/interfaces.ts#L1020-L1129

mortenson commented 7 years ago

Ah, I missed the interface before: https://github.com/ionic-team/stencil/blob/master/src/util/interfaces.ts#L1020

I don't think I'll be able to work on this, the time required to re-write all these methods, or make the Node packages work with v8js, is more than I have available.