How to import `renderToString` into a plain js/ts file

So, I have working serverless application, which has a handler.ts. Inside I atm do:
const html = `
<html>
<head>
<title>${item.title}</title>
// lots of meta tags
</head>
<body>
<h3>${item.title}</h3>
<audio controls>
<source src="${audioURL}" type="audio/mpeg">
</audio>
</body>
</html>
`,
const html = `
<html>
<head>
<title>${item.title}</title>
// lots of meta tags
</head>
<body>
<h3>${item.title}</h3>
<audio controls>
<source src="${audioURL}" type="audio/mpeg">
</audio>
</body>
</html>
`,
now I would like to use
import { renderToString } from "solid-js/web";
import App from './site/src/App';

const html = renderToString(() => <App item={item} />);
import { renderToString } from "solid-js/web";
import App from './site/src/App';

const html = renderToString(() => <App item={item} />);
which of course doesn't work, because it's tsx and uses < /> etc. is there a way I can build
import { renderToString } from "solid-js/web";
import App from './site/src/App';

const pageRenderer = (data: {id: string, item: string}) => {
const html = renderToString(() => <App id={data.id} event={data.item} />);
return html
}
export pagerenderer
import { renderToString } from "solid-js/web";
import App from './site/src/App';

const pageRenderer = (data: {id: string, item: string}) => {
const html = renderToString(() => <App id={data.id} event={data.item} />);
return html
}
export pagerenderer
into renderer.mjs that I can import into my serverless function?
28 Replies
Bersaelor
BersaelorOP2y ago
I was looking at https://www.solidjs.com/guides/server , and I really want to use what is written there, but I'm a big noob when it comes to esbuild or vite and how I can configure them to make something that I can import. Also, I'm obviously using yarn dev -> vite during the development of the html renderer, to allow for faster iteration, separate from the server-part my first attempt at a setup is:
lambda
│ tsconfig.json
│ package.json // with esbuild and @aws-sdk but no solid
│ lambda.ts // fetches data, imports the mjs from the ssg folder and is called on aws

└───site
│ │ package.json // with solid and vite in this folder so I can develop locally
│ | tsconfig.json
│ │ vite.config.ts.
│ │
│ └───src
│ │ index.tsx
│ │ App.tsx
│ │ ...

└───ssg
│ package.json // with solid, rollup and babel
│ index.js // import App from "../site/src/App";
│ rollup.config.js // so I can run rollup -c rollup.config.js to create the cjs file I can import in the lambda.ts
lambda
│ tsconfig.json
│ package.json // with esbuild and @aws-sdk but no solid
│ lambda.ts // fetches data, imports the mjs from the ssg folder and is called on aws

└───site
│ │ package.json // with solid and vite in this folder so I can develop locally
│ | tsconfig.json
│ │ vite.config.ts.
│ │
│ └───src
│ │ index.tsx
│ │ App.tsx
│ │ ...

└───ssg
│ package.json // with solid, rollup and babel
│ index.js // import App from "../site/src/App";
│ rollup.config.js // so I can run rollup -c rollup.config.js to create the cjs file I can import in the lambda.ts
unfortunately I'm stuck at the rollup expecting js and finding typescript built App.tsx Here's the rollup file I copied from solid-ssr
import nodeResolve from "@rollup/plugin-node-resolve";
import common from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";

export default [
{
input: "index.js",
output: [
{
dir: "/lib",
exports: "auto",
format: "cjs"
}
],
external: ["solid-js", "solid-js/web"],
plugins: [
nodeResolve({ preferBuiltins: true, exportConditions: ["solid", "node"] }),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
common()
]
}
];
import nodeResolve from "@rollup/plugin-node-resolve";
import common from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";

export default [
{
input: "index.js",
output: [
{
dir: "/lib",
exports: "auto",
format: "cjs"
}
],
external: ["solid-js", "solid-js/web"],
plugins: [
nodeResolve({ preferBuiltins: true, exportConditions: ["solid", "node"] }),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
common()
]
}
];
Alex Lohr
Alex Lohr2y ago
You need the babel-plugin-solid in order to run the jsx/tsx code on node. I wrote something similar in solid-register - you can even use that together with the server condition.
Bersaelor
BersaelorOP2y ago
Mhmm you mean in here https://github.com/atk/solid-register ? I can't find any reference to rollup in the repo. Nor to babel-plugin-solid
GitHub
GitHub - atk/solid-register
Contribute to atk/solid-register development by creating an account on GitHub.
Bersaelor
BersaelorOP2y ago
in order to run the jsx/tsx code on node
I don't think thats what I'm trying to do Mhmm, I can see that the App, I am importing in ssg/index.js is not actually jsx or tsx but just js : https://github.com/solidjs/solid/blob/main/packages/solid-ssr/examples/shared/src/components/App.js in the index I copied from https://github.com/solidjs/solid/blob/main/packages/solid-ssr/examples/ssg/index.js , it's imported as
import App from "../shared/src/components/App";
import App from "../shared/src/components/App";
so, after renaming my own test App.jsx to App.js the import worked not sure what to do about the App.tsx, since I really don't want to develop without ts so by turning away from typescript (for now) and renaming App.jsx to App.js I managed to build a ./lib/index.js that has the js methods I want. Could also import that into the lambda.ts and run sam build to get a lambda
Alex Lohr
Alex Lohr2y ago
solid-register uses babel-preset-solid. I originally wrote it to run solid tests on uvu. Another alternative should be jiti, if your tsconfig is working.
Alex Lohr
Alex Lohr2y ago
that could work, too; haven't tried it myself, though.
Bersaelor
BersaelorOP2y ago
I see, so I already had babel-preset-solid in my devDependencies inside the ssg/package.json as well as
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
set in the rollup.config.js
Alex Lohr
Alex Lohr2y ago
that should work.
Bersaelor
BersaelorOP2y ago
mhmm yeah, so thats what I had from the start of the above code, and when running rollup -c rollup.config.js I always get Error: Could not resolve '../site-js/src/App' from index.js that only goes away if I rename App.jsx to App.js but of course then I can't use it with vite anymore during testing ah maybe I'm missing something in the babel setting in the package.json mhmm no, I mean, I followed this one https://github.com/solidjs/solid/blob/main/packages/solid-ssr/package.json by the book and that one doesn't have a .babelrc nor a babel entry in the package.json. Not that adding babel to the package,json made any difference I could build a script that renames all *.jsx to *.js before doing rollup -c and afterwards names them back also seems like the solid-ssr sample just has App.js too, and no jsx
Alex Lohr
Alex Lohr2y ago
I think you need to tell rollup to support typescript, too. There's a babel-preset for that one, too.
Bersaelor
BersaelorOP2y ago
Are you aware of a working typescript-config for @rollup/plugin-typescript ? Whatever I try I always get [!] (plugin typescript) Error: @rollup/plugin-typescript: Couldn't process compiler options
Alex Lohr
Alex Lohr2y ago
What is in your tsconfig.json?
Bersaelor
BersaelorOP2y ago
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
// "types": ["vite/client"],
"noEmit": true,
"isolatedModules": true
}
}
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
// "types": ["vite/client"],
"noEmit": true,
"isolatedModules": true
}
}
I just copied it from the solid-js typescript folder to be honest
export default [
{
input: "index.jsx",
output: [
{
dir: "lib",
exports: "auto",
format: "cjs"
}
],
external: ["solid-js", "solid-js/web"],
plugins: [
typescript({ tsconfig: "./tsconfig.json" }),
nodeResolve({ preferBuiltins: true, exportConditions: ["solid", "node"] }),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
common()
]
}
];
export default [
{
input: "index.jsx",
output: [
{
dir: "lib",
exports: "auto",
format: "cjs"
}
],
external: ["solid-js", "solid-js/web"],
plugins: [
typescript({ tsconfig: "./tsconfig.json" }),
nodeResolve({ preferBuiltins: true, exportConditions: ["solid", "node"] }),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "ssr", hydratable: true }]]
}),
common()
]
}
];
is the rollup interesting, if I rename the index.jsx to index.tsx, it no longer complains about the typescript config, but rather says <App ... Error: Unexpected token (Note that you need plugins to import files that are not JavaScript) As a reminder, if I use index.jsx and then rename App.jsx to App.js it all builds successfully that is my baseline atm also, my index.jsx imports the js-solid, while the index.tsx imports the ts-solid. I have to separate folders with each one doing the same thing, just one in js and one in ts I mean, I know that https://github.com/solidjs/solid/blob/main/packages/solid-ssr/examples/ssr/rollup.config.js works with App.js, as that is the file also used in solid-ssr. I wonder if there exists a rollup.config.js somewhere that we know works with typescript Also, I think the typescript part might already executed before the unexpected-token error occurs:
$ rollup -c rollup.config.js

index.tsx → lib...
[!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
index.tsx (5:43)
3: // entry point for server render
4: export default async (req) => {
5: return await renderToStringAsync(() => <App id={req.id} event={req.event}/>);
^
6: };
7: //# sourceMappingURL=index.jsx.map
$ rollup -c rollup.config.js

index.tsx → lib...
[!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
index.tsx (5:43)
3: // entry point for server render
4: export default async (req) => {
5: return await renderToStringAsync(() => <App id={req.id} event={req.event}/>);
^
6: };
7: //# sourceMappingURL=index.jsx.map
since export default async (req) => already has the type stripped the @rollup/plugin-typescript docs https://github.com/rollup/plugins/tree/master/packages/typescript/#preserving-jsx-output also say:
Whenever choosing to preserve JSX output to be further consumed by another transform step via tsconfig compilerOptions by setting jsx: 'preserve' or overriding options, please bear in mind that, by itself, this plugin won't be able to preserve JSX output, usually failing with: To prevent that, make sure to use the acorn plugin, namely acorn-jsx, which will make Rollup's parser acorn handle JSX tokens.
after adding that in:
import jsx from 'acorn-jsx';
// ...
import typescript from '@rollup/plugin-typescript';

export default {
// … other options …
acornInjectPlugins: [jsx()],
plugins: [
typescript({ tsconfig: "./tsconfig.json" }), // with "jsx": "preserve"
// ...
};
import jsx from 'acorn-jsx';
// ...
import typescript from '@rollup/plugin-typescript';

export default {
// … other options …
acornInjectPlugins: [jsx()],
plugins: [
typescript({ tsconfig: "./tsconfig.json" }), // with "jsx": "preserve"
// ...
};
I no longer get the Unexpected token ( but I'm back at:
$ rollup -c rollup.config.js

index.tsx → lib...
[!] Error: Could not resolve '../site/src/App' from index.tsx
Error: Could not resolve '../site/src/App' from index.tsx
$ rollup -c rollup.config.js

index.tsx → lib...
[!] Error: Could not resolve '../site/src/App' from index.tsx
Error: Could not resolve '../site/src/App' from index.tsx
again, if I rename App.tsx to App.js it successfully imports, but then is of course confused by all the typescript inside it
Bersaelor
BersaelorOP2y ago
@lexlohr alright, so I built a minimal example https://github.com/Bersaelor/solid-rollup-aws-lambda so it's easier to discuss
GitHub
GitHub - Bersaelor/solid-rollup-aws-lambda: Using solid.js to serve...
Using solid.js to server-side-render html in a serverless environment - GitHub - Bersaelor/solid-rollup-aws-lambda: Using solid.js to server-side-render html in a serverless environment
Alex Lohr
Alex Lohr2y ago
comments are not allowed in JSON 🙂 remove // "types": ["vite/client"],
Bersaelor
BersaelorOP2y ago
I know, that was just in the discord comment, in the code I don't have that line anywhere. I just wanted to point out that the ssg is of course not using vite, since it's using rollup the minimal example on github illustrates it nicely, by default it all compiles, but: (1) if I rename App.js to App.jsx so I get jsx highlighting and can use vite for dev, it'll say Error: Could not resolve '../site/src/App' from index.jsx (2) if I link to the index.tsx instead of the index.jsx in the rollup.config.js , it'll also say Error: Could not resolve '../site/src/App' from index.tsx
Alex Lohr
Alex Lohr2y ago
Ah, ok Never actually tried using rollup and vite like that at the same time.
Bersaelor
BersaelorOP2y ago
I'm not married to rollup, anything that compiles the jsx into a mjs is fine with me. I just used rollup because thats what Ryans solid-ssr https://github.com/solidjs/solid/tree/main/packages/solid-ssr/examples/ssg uses all it really does is breaking down all the app-jsx code into a template in a js file: https://github.com/Bersaelor/solid-rollup-aws-lambda/blob/main/lambda/ssg/lib/index.js of course, in my production sample, that file is slightly bigger, but still very similar aws sam then bundles that module together with solid-js/web into a minified lambda.js that has no dependencies, atm my minified production lambda.js is only 27kb big and I only use vite while inside the site-js folder, to develop the website independently of the setup it sits in which you know, works, but I always have to rename all my *.js back to *.jsx to be able to use vite if you are also out of ideas, I might open an issue on https://github.com/solidjs/solid/issues "How to use solid-ssr with typescript". But i'll spend another hour googling what could cause the Error: Could not resolve '../site/src/App' from index.tsx first if I change the import to explicit App.tsx It still has issues with the typescript:
index.tsx: (2:17)

2 import App from "../site/src/App.tsx";
~~~~~~~~~~~~~~~~~~~~~

[!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
../site/src/App.tsx (1:12)
1: import type { Component } from 'solid-js';
^
index.tsx: (2:17)

2 import App from "../site/src/App.tsx";
~~~~~~~~~~~~~~~~~~~~~

[!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
../site/src/App.tsx (1:12)
1: import type { Component } from 'solid-js';
^
Alex Lohr
Alex Lohr2y ago
yep, rollup can't handle type imports.
Bersaelor
BersaelorOP2y ago
GitHub
SSR with typescript or jsx not working · solidjs/solid · Discussion...
So, I&#39;m building a serverless lambda function that uses solidjs for SSR. I&#39;ve based my code on the ssr-example in the solidjs repo. This example uses only js, in the examples/shared...
Bersaelor
BersaelorOP2y ago
I feel stupid doing, this but my latest solution is (from the package.json of the vite-typescript site)
"convert2js": "npx tsc --jsx preserve -t es2020 --outDir js --noEmit false",
"renameJsx": "for x in js/**/*.jsx; do mv \"$x\" \"${x%.jsx}.js\"; done",
"convert2js": "npx tsc --jsx preserve -t es2020 --outDir js --noEmit false",
"renameJsx": "for x in js/**/*.jsx; do mv \"$x\" \"${x%.jsx}.js\"; done",
the resulting site/js/src/App.js I then import into the file that is rolled up using rollup. And the result of that is built using esbuild
Alex Lohr
Alex Lohr2y ago
You don't need npx in npm scripts.
Bersaelor
BersaelorOP2y ago
the whole thing together from my root-aws-sam projects package.json:
"prepare": "cd src/lambda/site && yarn convert2js && yarn renameJsx && cd ../ssg && yarn rollup && cd ../../.. && sam build",
"prepare": "cd src/lambda/site && yarn convert2js && yarn renameJsx && cd ../ssg && yarn rollup && cd ../../.. && sam build",
Alex Lohr
Alex Lohr2y ago
Not cross-platform compatible (won't run on Windows), but if that doesn't bother you, it's okay.
Bersaelor
BersaelorOP2y ago
meh, not being able to import jsx is so annoying, now that I'm using it more I get stuff like:
[!] Error: Could not resolve './styles' from ../site/node_modules/@suid/material/index.jsx
[!] Error: Could not resolve './styles' from ../site/node_modules/@suid/material/index.jsx
guess I have to also go through the whole node_modules and rename everything to js alternatively, I could also add suid to the external in the rollup.config.js:
external: ["solid-js", "solid-js/web", "@suid/material/styles", "@suid/material"],
external: ["solid-js", "solid-js/web", "@suid/material/styles", "@suid/material"],
if I make suid external during the rollup, same as solid-js, the next step of esbuild will not be able to find it:
Error: NodejsNpmEsbuildBuilder:EsbuildBundle - Esbuild Failed:
[ERROR] Could not resolve "@suid/material"

ssg/lib/index.js:5:23:
5 │ var material = require('@suid/material');
~~~~~~~~~~~~~~~~

You can mark the path "@suid/material" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
Error: NodejsNpmEsbuildBuilder:EsbuildBundle - Esbuild Failed:
[ERROR] Could not resolve "@suid/material"

ssg/lib/index.js:5:23:
5 │ var material = require('@suid/material');
~~~~~~~~~~~~~~~~

You can mark the path "@suid/material" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
could this be an issue with the @ at the start?
'use strict';

var web = require('solid-js/web');
var styles = require('@suid/material/styles');
var material = require('@suid/material');
var solidJs = require('solid-js');
'use strict';

var web = require('solid-js/web');
var styles = require('@suid/material/styles');
var material = require('@suid/material');
var solidJs = require('solid-js');
because solid-js imports fine as part of the esbuild
Alex Lohr
Alex Lohr2y ago
No, the @ is just for npm namespaces, like the @solidjs namespace we registered.
Bersaelor
BersaelorOP2y ago
mhmm so I ended up just renaming everything in node_modules/suid extensively using:
"renameSuid0": "for x in node_modules/@suid/**/*.jsx; do mv \"$x\" \"${x%.jsx}.js\"; done"
"renameSuid0": "for x in node_modules/@suid/**/*.jsx; do mv \"$x\" \"${x%.jsx}.js\"; done"
etc seems to work this way, I just have to run yarn install everytime I want to use vite again
Want results from more Discord servers?
Add your server