How to import a default export of webpack entry file from outside? - node.js

I think I can best explain it with code. I have a file in webpack like the following:
import ReactDOMServer from 'react-dom/server';
import Server from './server';
import templateFn from './template';
export default (req, res) => {
const reactString = ReactDOMServer.renderToString(<Server />);
const template = templateFn(html);
res.send(template);
};
I also have an express application where I want to have access to the default exported function. If it makes any difference, this file is the webpack entry file. Here is what I tried in my express app:
const handleRequest = require(path.resolve(webpackConfig.output.path, webpackConfig.output.filename));
app.get('*', (req, res) => {
console.log(handleRequest);
});
I was trying to import the webpack generated file with the hope that I will be able to access the entry file's default export. Well, I was wrong as the output of the import was {}.
Is there a webpack plugin or some kind of a technique to do what I am trying to build? I don't want the express application to be part of the webpack build. That was the main reason I separated the code in this way.

I was able to access contents of webpack using library parameter (webpack.config.js):
output: {
path: ...,
filename: ...,
library: 'myapp',
libraryTarget: 'commonjs'
}
Then access it in the code:
const output = require(path.resolve(webpackConfig.output.path, webpackConfig.output.filename));
const defaultExportFunction = output.myapp.default;

Related

When using (require/import) axios, Cannot seem to use a JS custom function within Angular (.ts) component

I am trying to embed a JS custom function within my Angular (.ts) component.
The proccess i have gone through:
created and added a .js file with a function of my need
added the reference to this file on my angular.json scripts array
defined the function in my .ts component file
When i do this proccess with a .js file without require('axios') etc. it seems to work perfectly. but my .js file has some libraries (axios, express, cheerio) and when i use 'ng serve' i get an error at my console:
"Uncaught ReferenceError: require is not defined" - when using require
"Uncaught SyntaxError: Cannot use import statement outside a module" - when changing it to import
my .js file beginning:
// const axios = require('axios')
// const cheerio = require('cheerio')
// const express = require('express')
import axios from "axios"
import { Cheerio } from "cheerio"
import { Express } from "express"
const app = Express()
const axi = axios()
const cheer = cheerio()
function getScheduleFromUrl() {}
my .ts component references to the function:
declare function getScheduleFromUrl(): void;
constructor(private tvScheduleService: TvScheduleService) {
getScheduleFromUrl()
}
finally the reference in angular.json:
"src/assets/js/web-scrape-tv-schedule.js"]
Couldnt find a working example of this.
again: using .js function within my .ts component, while the .js file contains some exports

How to declare variable of type Express in TypeScript without importing express?

I have main file called main.ts where I imported express with
import express from 'express';
Then I have another class in separate file where I want to create method "init" which has one parameter named "app" of type Express. But somehow i can't say app:Express without importing express.
My goal is to import express only once and keep it in a main.ts file, and then in a main.ts file I will call "init" method ( from a separate file) where I will pass that imported express.
Main.ts file
import express from 'express';
import { FriendsRouter } from './routes/friends.router';
const app = express();
FriendsRouter.init(app);
const PORT:number = 3000;
app.listen(PORT,()=>{
console.log('Listening at '+PORT);
})
Spearete file (friends router in my case)
export class FriendsRouter {
private constructor(){
}
public static init(app:Express): void{
app.get('/friends',someMethod);
}
}
Problem is, in FriendsRouter file, I can't say app: Express.
How can I fix this ?
The usual thing is to import the type Express from the express package:
import { Express } from "express";
Note that that's just importing the type, not the function. The rest of your code is then fine as-is (including the type on app).
If you don't have that type, install the types for express like this:
npm install --save-dev #types/express
...but you've probably already done that.

How to use mysql2 library with Sapper?

I am creating an application in Svelte Sapper. I have a routes/account/login.js API route where I am trying to use mysql2. The route itself works (I checked with Postman), but as soon as I import mysql, the server crashes and an error appears:
[rollup-plugin-svelte] The following packages did not export their `package.json` file so we could not check the "svelte" field. If you had difficulties importing svelte components from a package, then please contact the author and ask them to export the package.json file.
- mysql2
import mysql from "mysql2/promise";
export async function post(req, res) {
//route test
const { login, password } = req.body;
res.end(`${login}, ${password}`);
}
What can I do to make this import work?
What can I do to make this import work?
The Sapper documentation doesn't say anything about whether you need to change something in the configuration additionally. https://sapper.svelte.dev/docs#Server_routes
I found a solution. I had to create a #lib folder in the src/node_modules folder and there file eg. db.js. In that file instead of import you need to use require()! and then you have to export the function that connects to the database. and then you can import that in the path
//src/node_modules/#lib/db.js
const mysql = require("mysql2");
export async function connectToDatabase() {
return mysql.createConnection({
host: "localhost",
....
})
}
//routes/account/login.js
import { query } from "#lib/db";
export async function post(req, res) {
...
}

How to achieve code splitting + SSR with #loadable/component ? Is webpack needed for the server code?

I'm trying to add code splitting + SSR to my React web app using #loadable/component.
My current situation: This is how I've implemented SSR for robots on my website. Since it's just for robots, I'm not using hydrate. Basically, I send either the index.html with the JS bundle's script tags for a regular user, or I send a fully rendered HTML page for the robots, without the need to hydrate.
My goal: I'd like to use #loadable/component to always return SSR pages from my server, and use hydrate to attach my JS bundle. And also achieve code splitting with that.
Here is how I currently build my app (pseudo code):
1. webpack BUILD FOR entry { app: "src/index.tsx" } AND OUTPUT BUNDLES TO MY /public FOLDER
2. babel TRANSPILE WHOLE `/src` FOLDER AND OUTPUT FILES TO MY /dist_app FOLDER
It's basically 2 builds, one of them is using webpack to bundle, and the other one basically transpiles the files from src to distApp.
And this is what my server does (pseudo code)
1. CHECK IF USER IS ROBOT (FROM THE USER AGENT STRING)
2. IF REGULAR USER
res.send("public/index.html"); // SEND REGULAR index.html WITH JS BUNDLES GENERATED BY WEBPACK
IF ROBOT
const App = require("./dist_app/App"); // THIS IS THE src/App COMPONENT TRANSPILED BY BABEL
const ssrHtml = ReactDOM.renderToString(<App/>);
// ADD <head> <helmet> <styles> ETC
res.send(ssrHtml);
The steps described above works just fine for my initial requirements (ssr just for robots).
But after I added #loadable/component to achieve code splitting + SSR, the set up above does not work anymore.
Because now I have dynamic imports on some of my routes. For example:
const AsyncPage = loadable(() => import("#app/pages/PageContainer"));
So my renderToString(<App/>) call comes back mostly empty, because it does not load those AsyncPages.
Over on the docs for Loadable components: server side rendering they have an example repo on how to achieve this.
But their example is kind of complex and it seems they are using webpack inside the server. I'll post what they do on their server below.
QUESTION
Do I really have to use webpack to bundle my app's server code in order to use #loadable/component for SSR like they are showing in their example? Can't I just use some kind of babel plugin to convert the dynamic imports into regular require calls? So that I'll be able to render it the way I was doing before?
It's weird, that they use webpack-dev-middleware. It's like this example should be used just for development. Does anybody know a repo with a production example of this?
import path from 'path'
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '#loadable/server'
const app = express()
app.use(express.static(path.join(__dirname, '../../public')))
if (process.env.NODE_ENV !== 'production') {
/* eslint-disable global-require, import/no-extraneous-dependencies */
const { default: webpackConfig } = require('../../webpack.config.babel')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
/* eslint-enable global-require, import/no-extraneous-dependencies */
const compiler = webpack(webpackConfig)
app.use(
webpackDevMiddleware(compiler, {
logLevel: 'silent',
publicPath: '/dist/web',
writeToDisk(filePath) {
return /dist\/node\//.test(filePath) || /loadable-stats/.test(filePath)
},
}),
)
}
const nodeStats = path.resolve(
__dirname,
'../../public/dist/node/loadable-stats.json',
)
const webStats = path.resolve(
__dirname,
'../../public/dist/web/loadable-stats.json',
)
app.get('*', (req, res) => {
const nodeExtractor = new ChunkExtractor({ statsFile: nodeStats })
const { default: App } = nodeExtractor.requireEntrypoint()
const webExtractor = new ChunkExtractor({ statsFile: webStats })
const jsx = webExtractor.collectChunks(<App />)
const html = renderToString(jsx)
res.set('content-type', 'text/html')
res.send(`
<!DOCTYPE html>
<html>
<head>
${webExtractor.getLinkTags()}
${webExtractor.getStyleTags()}
</head>
<body>
<div id="main">${html}</div>
${webExtractor.getScriptTags()}
</body>
</html>
`)
})
// eslint-disable-next-line no-console
app.listen(9000, () => console.log('Server started http://localhost:9000'))

Set "basedir" option for Pug in NestJS

I'm trying to use pug layouts in NestJS, however when extending a layout from an absolute path, pug requires the basedir option to be set.
In ExpressJS you would use app.locals.basedir = ..., what would be the equivalent in NestJS?
const server = await NestFactory.create<NestExpressApplication>(AppModule);
server.setViewEngine('pug');
server.setBaseViewsDir(join(__dirname, 'templates', 'views'));
await server.listen(config.server.port);
Using extends /layouts/index in a view would throw the following; the "basedir" option is required to use includes and extends with "absolute" paths.
I'm not looking to use relative paths, since this quickly becomes very messy. E.g. extends ../../../layouts/index
From what I can tell, you can achieve the same functionality as /layouts/index with just using layout/index so long as layout is a folder in your templates/views directory.
I've set up a git repo as a working example so you can test it out yourself and see if I need to go in more depth about anything.
EDIT 6/27/2019:
Thank you, I misunderstood your initial question.
With creating and express based application, you can send an express server to the NestFactory to use that server instance instead of having Nest create a plain instance for you. From here you can set up the express server as you normally would and get the desired functionality. I've modified the git repo to be able to test the scenario better and believe this is what you are looking for.
My main.ts
import { NestFactory } from '#nestjs/core';
import { NestExpressApplication, ExpressAdapter } from '#nestjs/platform-express';
import * as express from 'express';
import { AppModule } from './app.module';
import { join } from 'path';
async function bootstrap() {
// Creating and setting up the express instanced server
const server = express();
server.locals.basedir = join(__dirname, '..', 'views');
// Using the express server instance in the nest factory
const app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter(server));
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('pug');
await app.listen(3000);
}
bootstrap();
Overall the folder set up is like so
src
|-app.controller.ts
|-app.module.ts
|-app.service.ts
|-main.ts
views
|-hello
|-home.pug
|-message
|-message.pug
|-templates
|-layout.pug
And the beginning of my home.pug and message.pug files is extends /templates/layout
After looking around through the documentation, NestJS uses an express under the hood, and gives you access to the underlying instance with getHttpAdapter().getInstance().
Keeping that in mind, we can set the basedir as follows;
const express = server.getHttpAdapter().getInstance();
express.locals.basedir = join(__dirname, 'templates');

Resources