Resources:
Before submitting an issue, please consult our docs.
Stencil version: (run npm list @stencil/core from a terminal/cmd prompt and paste output below):
scott@ax ~/workspace/my-app master
[49] → npm list @stencil/core
@stencil/[email protected] /Users/scott/workspace/my-app
└── @stencil/[email protected]
I'm submitting a ... (check one with "x")
[x] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/
Current behavior:
Following the getting started guide from the docs, then pasting the SSR example from the docs into a file generates the following error:
scott@ax ~/workspace/my-app master
[48] → node server.js
module.js:471
throw err;
^
Error: Cannot find module '/Users/scott/workspace/my-app/node_modules/@stencil/core/bin/util'
at Function.Module._resolveFilename (module.js:469:15)
at Function.Module._load (module.js:417:25)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at validateRendererConfig (/Users/scott/workspace/my-app/node_modules/@stencil/core/dist/server/index.js:5045:22)
at Object.createRenderer (/Users/scott/workspace/my-app/node_modules/@stencil/core/dist/server/index.js:4930:5)
at Object.<anonymous> (/Users/scott/workspace/my-app/server.js:9:24)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
Expected behavior:
I expect the example to work. 😉
Steps to reproduce:
git clone https://github.com/ionic-team/stencil-starter.git my-app
cd my-app
git remote rm origin
npm install
npm start
npm run build
then paste the SSR example from the docs into ./server.js and run node server.js
Related code:
var express = require('express');
var app = express();
var fs = require('fs');
var path = require('path');
var stencil = require('@stencil/core');
// Create the stencil SSR renderer
var renderer = stencil.createRenderer({
rootDir: path.join(__dirname, './'),
buildDir: path.join(__dirname, './www/build/'),
namespace: 'app',
logLevel: 'debug'
});
// host the build directory as static files
// so the app can pull client side scripts
app.use('/build', express.static('www/build'));
// If you want to use HTML5 style routing in your client, keep the catch-all route handler here,
// otherwise change it to a more specific route
app.get('/*', function (req, res, next) {
console.log(`serve: ${req.url}`);
var filePath = path.join(__dirname, 'www/index.html');
fs.readFile(filePath, 'utf-8', (err, html) => {
if (err) {
console.error(err);
res.send(err);
return;
}
// Render the initial app content through Stencil
renderer.hydrateToString({
html: html,
req: req,
config: {}
}, function(err, html) {
if (err) {
// Handle the error hydrating
console.error(err);
return res.sendStatus(500);
}
// Send the hydrated data back
res.send(html);
});
});
});
Other information:
Big fan of what you guys are doing and excited to see where it goes!
Thanks for filing the issue! We'll take a look and make sure to get this up and running. Also we're building out an SSR starter to be document how it works.
Code I'm using that still isn't working:
var express = require('express');
var app = express();
var fs = require('fs');
var path = require('path');
var stencil = require('@stencil/core');
// Create the stencil SSR renderer
var renderer = stencil.createRenderer({
rootDir: path.join(__dirname, './'),
buildDir: path.join(__dirname, './www/build/'),
namespace: 'app',
logLevel: 'debug'
});
// host the build directory as static files
// so the app can pull client side scripts
app.use('/build', express.static('www/build'));
// If you want to use HTML5 style routing in your client, keep the catch-all route handler here,
// otherwise change it to a more specific route
app.get('/*', function (req, res, next) {
console.log(`serve: ${req.url}`);
var filePath = path.join(__dirname, 'www/index.html');
fs.readFile(filePath, 'utf-8', (err, html) => {
if (err) {
console.error(err);
res.send(err);
return;
}
// Render the initial app content through Stencil
renderer.hydrateToString({
html: html,
req: req,
config: {}
}, function(err, html) {
if (err) {
// Handle the error hydrating
console.error(err);
return res.sendStatus(500);
}
// Send the hydrated data back
res.send(html);
});
});
});
app.listen(8080);
@adamdbradley I'm still unable to get the SSR example working–any chance you can point me in the right direction?
Added some logging to my script above, and it appears the script is getting caught on renderer.hydrateToString–here's my current script:
var express = require('express');
var app = express();
var fs = require('fs');
var path = require('path');
var stencil = require('@stencil/core');
// Create the stencil SSR renderer
var renderer = stencil.createRenderer({
rootDir: path.join(__dirname, './'),
buildDir: path.join(__dirname, './www/build/'),
namespace: 'app',
logLevel: 'debug'
});
// host the build directory as static files
// so the app can pull client side scripts
app.use('/build', express.static('www/build'));
// If you want to use HTML5 style routing in your client, keep the catch-all route handler here,
// otherwise change it to a more specific route
app.get('/*', function (req, res, next) {
console.log(`serve: ${req.url}`);
var filePath = path.join(__dirname, 'www/index.html');
fs.readFile(filePath, 'utf-8', (err, html) => {
if (err) {
console.error(err);
res.send(err);
return;
}
// Render the initial app content through Stencil
console.log('Hydrating...');
renderer.hydrateToString({
html: html,
req: req,
config: {}
}, function(err, html) {
console.log('Hydrated!', html);
if (err) {
// Handle the error hydrating
console.error(err);
return res.sendStatus(500);
}
// Send the hydrated data back
console.log('Sending response!');
res.send(html);
});
});
});
app.listen(8080);
In the logs I see Hydrating... but never Hydrated! or Sending response!
Ok, we'll reopen and take a look. Thanks for providing some example code.
I opened a PR to fix this in the docs a few days ago: https://github.com/ionic-team/stencil-site/pull/55
Let me know if the changes to the example help you out.
You can use the middleware now, like this example: https://github.com/ionic-team/stencil-ssr-starter/blob/master/server.js
Also hydrateToString now only returns a promise:
https://github.com/ionic-team/stencil/blob/eef5d0f2f7bc75d781f34f3d376af8756382f304/src/server/express-middleware.ts#L28
We'll get the docs updated, thanks!
@adamdbradley Correct me if I'm wrong. I'm expecting Stencil's SSR to SSR all the pages at any given available routes in the web app.
It seems like the current SSR only render the home page at route / at all times when Javascript is disabled on a browser. It will still show the home page even when I'm already at route /about. Curious to know if this is not what it supposed to do.
Update: Just found out that it only works when --prerender is specified for the case to serve page on specific route when Javascript is disabled on a browser. https://github.com/ionic-team/stencil-ssr-starter/blob/master/package.json#L12 does not prerender all pages.
I was also expecting the same behavior as @motss (preventing the flash of homepage content), and his suggested fix, the --prerender flag, worked for me.