Newbie is Here!
I am developing media server for sfu.
I want to make sfu media server, so i think it needs RTCPeerconnection like client side.
myProblem is that 'wrtc.RTCPeerConnection.createOffer()' is not work in nestjs
I want to use webrtc api in nestjs like
import * as webrtc from 'wrtc'
const Peer = new webrtc.RTCPeerConnection();
Peer.createOffer();
I want to use methods (createOffer, createAnswer ...etc) and events (onicecandidate, ontrack ...etc)
but it is not working.
It is result of wrtc.RTCPeerConnection().createOffer() in my project
It make "offer" of empty object like this
Promise { <pending> }
in console.log()
there is my code of nestjs
sfu.service.ts
import { Injectable } from '#nestjs/common';
import { Server, Socket } from 'socket.io';
import * as DefaultRTCPeerConnection from 'wrtc';
const {
RTCPeerConnection,
} = DefaultRTCPeerConnection;
#Injectable()
export class SfuService {
private peers: Map<string, RTCPeerConnection>;
private id: string;
constructor() {
this.peers = new Map();
}
getter() {
return this.peers.get(this.id);
}
async createPeerConnection(connectionId) {
const peerConnection = new RTCPeerConnection();
this.peers.set(connectionId, peerConnection);
this.id = connectionId;
// peerConnection.ontrack = (stream) => this.onTrackHandler(stream);
return peerConnection;
}
async createOffer(id, option) {
try {
// Todo option setting
const offer = await this.peers.get(id).createOffer(option);
return offer;
} catch (e) {
console.log(e);
}
}
async createAnswer(id, option) {
try {
// Todo option setting
const answer = await this.peers.get(id).createAnswer(option);
return answer;
} catch (e) {
console.log(e);
}
}
async setLocalDescription(description) {
try {
await this.peers.get(this.id).setLocalDescription(description);
} catch (e) {
console.log(e);
}
}
async setRemoteDescription(description) {
try {
await this.peers.get(this.id).setRemoteDescription(description);
} catch (e) {
console.log(e);
}
}
}
and then my gateway code
sfu.gateway.ts
import { UsePipes, ValidationPipe } from '#nestjs/common';
import {
SubscribeMessage,
WebSocketGateway,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
} from '#nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { OfferDto } from 'src/wss/dto';
import { offerValidationPipe } from 'src/wss/pipes';
import { SfuService } from './sfu.service';
import * as DefaultRTCPeerConnection from 'wrtc';
const { RTCPeerConnection } = DefaultRTCPeerConnection;
#WebSocketGateway({
namespace: 'sfu',
cors: {
origin: [
'http://localhost:4000',
'http://localhost:3300',
'http://localhost:5000',
'http://localhost:3000',
],
},
})
export class SfuGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
constructor(private service: SfuService) {}
server: Server;
afterInit(_server: Server) {
this.server = _server;
}
handleConnection(socket: Socket, ...args: any[]) {
console.log(socket.id, 'Connected!');
this.service.Test();
}
handleDisconnect(socket: Socket) {
console.log(socket.id, 'DisConnected!');
}
#UsePipes(new ValidationPipe())
#SubscribeMessage('join_room')
async JoinRoom(socket: Socket, roomName: string) {
socket.join(roomName);
socket.emit('welcome', socket.id);
return {};
}
#UsePipes(new offerValidationPipe())
#SubscribeMessage('offer')
async Offer(socket: Socket, data: OfferDto) {
this.service.createPeerConnection(socket.id);
const connection = this.service.getter();
connection.onicecandidate = (e) => {
socket.emit('ice', e.candidate, socket.id);
};
const t = new RTCPeerConnection();
t.setRemoteDescription(data.offer);
console.log('offer Set!', data.offer);
await this.service.setRemoteDescription(data.offer);
console.log('Offer Set Complete\n');
const temp = await this.service.createAnswer(socket.id, {
offerToReceiveAudio: false,
offerToReceiveVideo: false,
});
console.log('Answer ppppppppppp : ', temp);
this.service.setLocalDescription(temp);
const offer = this.service.createOffer(socket.id, {
offerToReceiveAudio: false,
offerToReceiveVideo: false,
});
console.log(offer);
socket.emit('test', offer);
socket.to(data.roomName).emit('offer', data.offer, socket.id);
}
}
my package.json
{
"name": "socketserver",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"#nestjs/common": "^8.0.0",
"#nestjs/core": "^8.0.0",
"#nestjs/platform-express": "^8.0.0",
"#nestjs/platform-socket.io": "^9.0.3",
"#nestjs/websockets": "^9.0.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"nestjs-socket-handlers-with-ack": "^2.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.6",
"rxjs-compat": "^6.6.7",
"socket.io-redis": "^6.1.1",
"wrtc": "^0.4.7"
},
"devDependencies": {
"#nestjs/cli": "^8.0.0",
"#nestjs/schematics": "^8.0.0",
"#nestjs/testing": "^8.0.0",
"#types/express": "^4.17.13",
"#types/jest": "27.5.0",
"#types/node": "^16.0.0",
"#types/socket.io": "^3.0.2",
"#types/supertest": "^2.0.11",
"#types/ws": "^8.5.3",
"#typescript-eslint/eslint-plugin": "^5.0.0",
"#typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.0.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.1",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
Please Help me!!
I have a problem when i updated in my project the webpack package to V5.x.
Here is the error :
webpack-hot-client: entry Object values must be an Array or Function. Please check your webpack config.
Here is my package.json :
"*.css",
"*.scss",
"*.sass" ], "main": "index.js", "scripts": {
"analyze": "bnr clean:build && bnr analyze",
"build:all": "bnr clean:build && bnr build:client && bnr build:server",
"build:client": "bnr clean:build && bnr build:client",
"build:server": "bnr clean:build && bnr build:server",
"clean": "bnr clean:build",
"dev": "bnr dev",
"docker": "yarn docker:build && yarn docker:start && yarn docker:status",
"docker:build": "docker-compose build --no-cache",
"docker:start": "docker-compose up -d",
"docker:status": "docker-compose logs -f -t",
"flow": "bnr flow",
"flow:stop": "bnr flow:stop",
"lint": "npm-run-all lint:js lint:style lint:json",
"lint:js": "bnr lint:js",
"lint:style": "bnr lint:style",
"lint:json": "bnr lint:json",
"prod": "bnr build:client && bnr build:server && bnr start",
"start": "bnr start",
"sitemap": "node sitemap-generator.js" }, "betterScripts": {
"analyze": {
"command": "npx webpack -p --progress --hide-modules --config ./tools/webpack/production.client.babel.js",
"env": {
"NODE_ENV": "analyze"
}
},
"build:client": {
"command": "npx webpack --hide-modules --config ./tools/webpack/production.client.babel.js && npx gulp --gulpfile
tools/gulpfile.js",
"env": {
"NODE_ENV": "production"
}
},
"build:server": {
"command": "npx babel ./src -d ./dist --copy-files && npx webpack --hide-modules --config
./tools/webpack/production.server.babel.js",
"env": {
"NODE_ENV": "production"
}
},
"clean:build": {
"command": "rimraf ./public/assets && rimraf ./public/webpack-assets.json"
},
"dev": {
"command": "nodemon ./index.js",
"env": {
"NODE_PATH": "./src",
"NODE_ENV": "development",
"PORT": 3000
}
},
"flow": {
"command": "npx flow"
},
"flow:stop": {
"command": "npx flow stop"
},
"lint:js": {
"command": "npx eslint --fix ./src ./tools ./index.js"
},
"lint:json": {
"command": "npx prettier --write src/**/**/*.json"
},
"lint:style": {
"command": "npx stylelint --fix ./src/**/*.css, ./src/**/*.scss"
},
"start": {
"command": "node ./index.js",
"env": {
"NODE_PATH": "./dist",
"NODE_HOST": "0.0.0.0",
"NODE_ENV": "production",
"PORT": 8080
}
} }, "husky": {
"hooks": {
"pre-commit": "lint-staged"
} }, "lint-staged": {
"*.{js,jsx}": "eslint --fix",
"*.{json,md}": "prettier --write",
"*.{scss,sass}": "stylelint --syntax=scss" }, "nodemonConfig": {
"watch": [
"src/server.js",
"src/handler.js",
"src/utils/renderHtml.js",
"src/theme/variables.scss"
] }, "browserslist": [
"> 1%",
"last 3 versions" ], "eslintIgnore": [
"tools/flow",
"public/assets" ], "dependencies": {
"#babel/cli": "^7.14.5",
"#babel/plugin-proposal-export-namespace-from": "^7.5.2",
"#fortawesome/fontawesome-free": "^5.15.1",
"#hapi/address": "^4.1.0",
"#koa/router": "^10.1.1",
"#loadable/component": "^5.10.2",
"#loadable/server": "^5.10.2",
"#tweenjs/tween.js": "^18.3.1",
"ansi-regex": "^6.0.1",
"axios": "^0.23.0",
"bootstrap": "^5.1.3",
"chalk": "^4.1.2",
"classnames": "^2.2.6",
"d3-ease": "^3.0.1",
"del": "^6.0.0",
"esm": "^3.2.25",
"glob-parent": "^6.0.2",
"gulp": "^4.0.2",
"gulp-cache": "^1.1.3",
"gulp-imagemin": "^8.0.0",
"gulp-postcss": "^9.0.0",
"gulp-rename": "^2.0.0",
"i18next": "^21.3.2",
"i18next-browser-languagedetector": "^6.1.2",
"i18next-node-fs-backend": "^2.1.3",
"i18next-resource-store-loader": "^0.1.2",
"i18next-xhr-backend": "^3.1.2",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.2",
"koa": "^2.8.1",
"koa-bodyparser": "^4.2.1",
"koa-compress": "^5.1.0",
"koa-favicon": "^2.0.1",
"koa-helmet": "^6.1.0",
"koa-i18next-detector": "^0.7.2",
"koa-i18next-middleware": "^1.1.12",
"koa-i18next-middleware-fixed": "^1.1.10-b3",
"koa-morgan": "^1.0.1",
"koa-mount": "^4.0.0",
"koa-router": "^10.1.1",
"koa-static": "^5.0.0",
"koa-webpack": "^6.0.0",
"koa-webpack-server": "^0.2.4",
"micro-dash": "^8.1.0",
"moment": "^2.24.0",
"p-min-delay": "^4.0.1",
"pm2": "^5.1.2",
"qs": "^6.8.0",
"rc-animate": "^3.1.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.6.1",
"rc-tween-one": "^2.6.3",
"react": "^17.0.2",
"react-bootstrap": "^1.0.0-beta.8",
"react-dom": "npm:#hot-loader/react-dom#^16.8.6",
"react-global-configuration": "^1.4.1",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-i18next": "^11.12.0",
"react-icons": "^4.3.1",
"react-motion": "^0.5.2",
"react-player": "^2.9.0",
"react-pose": "^4.0.8",
"react-router": "^5.0.1",
"react-router-config": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-router-last-location": "^2.0.1",
"react-router-sitemap": "^1.2.0",
"react-spring": "^9.3.0",
"react-tilt": "^0.1.4",
"react-typist": "^2.0.5",
"react-youtube": "^7.9.0",
"sass-resources-loader": "^2.0.1",
"serialize-javascript": "^6.0.0",
"styled-components": "^5.3.1",
"terser-webpack-plugin": "^5.2.4" }, "devDependencies": {
"#babel/core": "^7.6.0",
"#babel/node": "^7.14.5",
"#babel/plugin-proposal-class-properties": "^7.5.5",
"#babel/plugin-proposal-export-default-from": "^7.5.2",
"#babel/plugin-proposal-optional-chaining": "^7.6.0",
"#babel/plugin-syntax-dynamic-import": "^7.2.0",
"#babel/preset-env": "^7.14.5",
"#babel/preset-flow": "^7.0.0",
"#babel/preset-react": "^7.14.5",
"#babel/register": "^7.6.0",
"#babel/runtime": "^7.6.0",
"#loadable/babel-plugin": "^5.10.0",
"#loadable/webpack-plugin": "^5.7.1",
"asset-require-hook": "^1.2.0",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-plugin-dynamic-import-node": "^2.3.0",
"babel-plugin-istanbul": "^6.1.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-plugin-transform-remove-console": "^6.9.4",
"better-npm-run": "^0.1.1",
"compression-webpack-plugin": "^9.0.0",
"core-js": "3",
"css-loader": "^6.4.0",
"css-modules-require-hook": "^4.2.3",
"cssnano": "^5.0.8",
"eslint": "^8.0.1",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-webpack": "^0.13.1",
"eslint-plugin-flowtype": "^6.1.1",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.14.3",
"eslint-plugin-react-hooks": "^4.2.0",
"file-loader": "^6.2.0",
"flow-bin": "^0.162.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"html-minifier": "^4.0.0",
"husky": "^7.0.2",
"imagemin-webpack-plugin": "^2.4.2",
"lint-staged": "^11.2.3",
"mini-css-extract-plugin": "^2.4.2",
"nodemon": "^2.0.13",
"normalize.css": "^8.0.1",
"npm-run-all": "^4.1.5",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"postcss": "^8.3.9",
"postcss-loader": "^6.2.0",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.4.1",
"react-dev-utils": "^11.0.4",
"react-hot-loader": "^4.12.13",
"react-router-sitemap-generator": "^0.0.8",
"react-test-renderer": "^17.0.2",
"rimraf": "^3.0.0",
"sass": "^1.43.2",
"sass-loader": "^12.2.0",
"static-site-generator-webpack-plugin": "^3.4.2",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended-scss": "^4.3.0",
"stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.10.1",
"url-loader": "^4.1.1",
"webpack": "^5.58.2",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "4.9.1",
"webpack-dev-middleware": "^5.2.1",
"webpack-hot-middleware": "^2.25.0",
"webpack-manifest-plugin": "^4.0.2",
"webpack-merge": "^5.8.0",
"webpack-node-externals": "^3.0.0" }, "engines": {
"node": ">=8",
"npm": ">=5" } }```
Here is my base.config.js of webpack :
import webpack from 'webpack'
import { WebpackManifestPlugin } from 'webpack-manifest-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import LoadablePlugin from '#loadable/webpack-plugin'
import config from './config'
const nodeEnv = process.env.NODE_ENV || 'development'
const isDev = nodeEnv === 'development'
const getPlugins = () => {
const plugins = [
new WebpackManifestPlugin({
fileName: path.resolve(process.cwd(), 'public/webpack-assets.json'),
filter: file => file.isInitial
}),
new MiniCssExtractPlugin({
filename: `${config.cssFileName}.css`,
chunkFilename: `${config.cssChunkFileName}.css`,
ignoreOrder: true
}),
// Setup environment variables for client
new webpack.EnvironmentPlugin({ NODE_ENV: JSON.stringify(nodeEnv) }),
// Setup global variables for client
new webpack.DefinePlugin({
__CLIENT__: true,
__SERVER__: false,
__DEV__: isDev
}),
new LoadablePlugin({ filename: '../loadable-stats.json', writeToDisk: true })
]
if (isDev) {
plugins.push(new FriendlyErrorsWebpackPlugin())
}
return plugins
}
// Webpack configuration
module.exports = {
mode: isDev ? 'development' : 'production',
devtool: isDev ? 'eval-source-map' : 'hidden-source-map',
context: path.resolve(process.cwd()),
entry: ['webpack-hot-middleware/client','./src/index.js'],
optimization: {
splitChunks: {
chunks: 'async'
}
},
output: {
path: path.resolve(process.cwd(), 'public/assets'),
publicPath: '/assets/',
filename: `${config.fileName}.js`,
chunkFilename: `${config.chunkFileName}.js`,
pathinfo: isDev
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
options: { cacheDirectory: isDev }
},
{
test: /\.css$/,
include: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css',
options: {
importLoaders: 1,
modules: false,
sourceMap: true
}
},
{ loader: 'postcss', options: { sourceMap: true } }
]
},
{
test: /\.(scss|sass)$/,
exclude: path.resolve(__dirname, '..', '..', 'src/theme/'),
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: isDev,
reloadAll: true
}
},
{
loader: 'css',
options: {
importLoaders: 2,
modules: {
localIdentName: config.cssModulesIdentifier,
context: path.resolve(process.cwd(), 'src')
},
sourceMap: true
}
},
{ loader: 'postcss', options: { sourceMap: true } },
{
loader: 'sass',
options: {
sassOptions: {
outputStyle: 'expanded'
},
sourceMap: true
}
},
{
loader: 'sass-resources',
options: {
resources: path.resolve(process.cwd(), 'src/theme/_include.scss')
}
}
]
},
{
test: /\.(scss|sass)$/,
include: path.resolve(__dirname, '..', '..', 'src/theme/'),
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: isDev,
reloadAll: true
}
},
{
loader: 'css',
options: {
importLoaders: 2,
modules: false,
sourceMap: true
}
},
{ loader: 'postcss', options: { sourceMap: true } },
{
loader: 'sass',
options: {
sassOptions: {
outputStyle: 'expanded'
},
sourceMap: true
}
}
]
},
{
test: path.resolve(process.cwd(), 'src/locales'),
loader: 'i18next-resource-store-loader'
},
{
test: /\.(woff2?|ttf|eot|svg|otf)$/,
loader: 'url',
options: { limit: 10240, name: config.staticFilesName }
},
{
test: /\.(gif|png|jpe?g|webp)$/,
// Any image below or equal to 10Kb will be converted to base64
loader: 'url',
options: { limit: 10240, name: config.staticFilesName }
},
{
test: /\.(mp3|mp4|ogv)$/,
loader: 'file',
options: { name: config.staticFilesName }
}
]
},
plugins: getPlugins(),
resolveLoader: {
mainFields: ['loader']
},
resolve: {
modules: ['src', 'node_modules'],
descriptionFiles: ['package.json'],
extensions: ['.js', '.jsx', '.json'],
fallback:{
fs: false,
vm: false,
net: false,
tls: false
}
},
cache: isDev,
stats: { children: false }
I tried a lot of things and i fixed a lot of issues but for me the entry is an Array, so i don't know why i got this error.
Here there is my server.js (he launches all the programs) :
import logger from 'koa-morgan'
import Koa from 'koa'
import bodyParser from 'koa-bodyparser'
import compression from 'koa-compress'
import helmet from 'koa-helmet'
import querystring from 'qs'
import Router from '#koa/router'
import favicon from 'koa-favicon'
import axios from 'axios'
import chalk from 'chalk'
import mount from 'koa-mount'
import serve from 'koa-static'
import Backend from 'i18next-node-fs-backend'
import i18n from 'i18next'
import i18nextMiddleware from 'koa-i18next-middleware-fixed'
import LanguageDetector from 'koa-i18next-detector'
import c from './config'
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0
const app = new Koa()
const router = new Router()
const sendMessage = body =>
new Promise((resolve, reject) => {
const config = {
maxRedirects: 0,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
axios
.post(
'https://go.pardot.com/l/138941/2019-07-22/2h6dgb',
querystring.stringify({ ...body, privacy: true }),
config
)
.then(result => {
resolve(result.status === 200)
})
.catch(e => {
reject(new Error(e))
})
})
const lngDetector = new LanguageDetector()
i18n
.use(Backend)
.use(lngDetector)
.init(
{
debug: false,
fallbackLng: 'fr',
saveMissing: false,
react: {
useSuspense: false
},
detection: {
order: ['path', 'navigator']
},
interpolation: {
escapeValue: false,
formatSeparator: ',',
format: (value, format) => {
if (format === 'uppercase') return value.toUpperCase()
return value
}
},
preload: ['fr'],
load: 'languageOnly',
ns: [
'global',
'landing_references',
'landing_expertises',
'home',
'references',
'expertises',
'contact',
'about',
'partners',
'team'
],
defaultNS: 'global',
backend: {
loadPath: `${path.resolve(process.cwd(), 'src')}/locales/{{lng}}/{{ns}}.json`
}
},
async () => {
// Use helmet to secure Express with various HTTP headers
app.use(helmet())
app.use(bodyParser())
// Compress all requests
app.use(compression())
// Use for http request debug (show errors only)
app.use(logger('dev', { skip: ctx => ctx.status < 400 }))
app.use(favicon(path.resolve(process.cwd(), 'public/favicon.ico')))
app.use(i18nextMiddleware.getHandler(i18n, { locals: 'lng' }))
// Docker serve static trough nginx for better performance
if (!__DOCKER__) {
console.log(chalk.magenta('==> 🐌 Serve statics with koa'))
app.use(mount('/locales', serve(path.resolve(process.cwd(), 'src/locales'))))
app.use(serve(path.resolve(process.cwd(), 'public')))
}
if (__DEV__) {
app.use(mount('/images', serve(path.resolve(process.cwd(), 'src/images'))))
/* Run express as webpack dev server */
const webpack = require('webpack')
const webpackConfig = require('../tools/webpack/base.config')
const compiler = webpack(webpackConfig)
const koaWebpack = require('koa-webpack')
new webpack.ProgressPlugin().apply(compiler)
const options = {
compiler,
devMiddleware: {
publicPath: webpackConfig.output.publicPath,
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true,
writeToDisk: false,
quiet: true, // Turn it on for friendly-errors-webpack-plugin
noInfo: true,
stats: 'minimal',
serverSideRender: true
}
}
const middleware = await koaWebpack(options)
app.use(middleware)
}
router
.post('/contact', async ctx => {
try {
await sendMessage(ctx.request.body)
ctx.body = {
message: 'Votre message a bien été envoyé'
}
ctx.status = 200
} catch (e) {
ctx.body = {
message: 'Une erreur est survenue'
}
ctx.status = 500
}
})
.get('/', async ctx => {
ctx.status = 302
return ctx.redirect(`/${ctx.request.language}`)
})
.get('*', async (ctx, next) => {
return require('./render')(ctx, next)
})
app.use(router.routes()).use(router.allowedMethods())
if (c.port) {
app.listen(c.port, c.host, err => {
const url = `http://${c.host}:${c.port}`
if (err) console.error(chalk.red(`\n==> Error happen ${err}`))
console.info(chalk.green(`\n==> 🌎 Listening at ${url}`))
})
} else {
console.error(chalk.red('\n==> Error : No PORT environment variable has been specified'))
}
}
)
UPDATE :
I put some console.log in the compiler file :
for (const key of Object.keys(entry)) {
const value = entry[key];
console.log(entry)
console.log(value)
console.log(Array.isArray(Object.values(value)))
if (!Array.isArray(value)) {
throw new TypeError(
'webpack-hot-client: `entry` Object values must be an Array or Function. Please check your webpack config.'
);
Console.log return this :
main: { import: [ 'webpack-hot-middleware/client', './src/index.js' ] }
}
{ import: [ 'webpack-hot-middleware/client', './src/index.js' ] }
true
So i think that there is a problem with the declaration of variable, because we have to do an Object.values() to get the array.
So idk what can i do to resolve the problem, i will not change the dependencies files...
Thx for your time and your response!
Regards
It might not be the right example to your situation, but they made some internal changes to the webpack structure:
entry: {} allows an empty object now (to allow to use plugins to add entries)
here's one of the newer examples I found:
module.exports = {
entry: {
about: { import: './about.js', filename: 'pages/[name][ext]' },
},
};
Source: Webpack v5 internal changes: Entry
I found the error. WebPack Hot Client is not update for the last version of webpack (5.x). If you use Webpack Hot Client, you have to use version 4 of Webpack.
Regards
I keep getting this error but don't know why. If anyone could help me solve it, that would make my day 😁
Error: Nest can't resolve dependencies of the MovieService (?). Please make sure that the argument MovieRepository at index [0] is available in the MovieService context.
This is the structure of my MovieService:
import {Injectable} from "#nestjs/common";
import {InjectRepository} from "#nestjs/typeorm";
import {Repository} from "typeorm";
import {CreateMovieDto} from "./dto/create-movie.dto";
import {Movie} from "./movie.entity";
#Injectable()
export class MovieService {
constructor(
#InjectRepository(Movie)
private readonly movieRepository: Repository<Movie>
) {}
create(createMovieDto: CreateMovieDto): Promise<Movie> {
const movie = new Movie();
movie.path = createMovieDto.path;
movie.title = createMovieDto.title;
movie.originalTitle = createMovieDto.originalTitle;
movie.poster = createMovieDto.poster;
movie.descr = createMovieDto.descr;
movie.year = createMovieDto.year;
return this.movieRepository.save(movie);
}
async findAll(): Promise<Movie[]> {
return this.movieRepository.find();
}
findOne(id: string): Promise<Movie> {
return this.movieRepository.findOne(id);
}
findByPath(path: string): Promise<Movie> {
return this.movieRepository.findOne({path});
}
async remove(id: string): Promise<void> {
await this.movieRepository.delete(id);
}
}
The MovieModule :
import {Module} from "#nestjs/common";
import {TypeOrmModule} from "#nestjs/typeorm";
import {Movie} from "./movie.entity";
import {MovieService} from "./movie.service";
#Module({
imports: [TypeOrmModule.forFeature([Movie])],
providers: [MovieService],
exports: [MovieService],
})
export class MovieModule {}
The Movie entity :
import {Column, Entity, PrimaryGeneratedColumn} from "typeorm";
#Entity()
export class Movie {
#PrimaryGeneratedColumn()
id: number;
#Column()
path: string;
#Column()
title: string;
#Column()
originalTitle: string;
#Column()
poster: string;
#Column()
descr: string;
#Column()
year: string;
}
And the app module :
import {Module} from "#nestjs/common";
import {TypeOrmModule} from "#nestjs/typeorm";
import {AppController} from "./app.controller";
import {AppService} from "./app.service";
import {MovieModule} from "./movie/movie.module";
#Module({
imports: [
TypeOrmModule.forRoot({
type: "mysql",
host: "localhost",
port: 3306,
username: "*****",
password: "*****",
database: "*****",
autoLoadEntities: true,
synchronize: true,
}),
MovieModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Edit 1
I tried copy-pasting the movie folder into a new nest project and it works. I'm even more confused on why I get this error
Here's my package.json
{
"name": "marineris",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"#nestjs/common": "^8.0.0",
"#nestjs/core": "^8.0.0",
"#nestjs/platform-express": "^8.0.0",
"#nestjs/typeorm": "^8.0.2",
"bcrypt": "^5.0.1",
"cookie-parser": "^1.4.5",
"hbs": "^4.1.2",
"mysql2": "^2.3.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"sync-request": "^6.1.0",
"typeorm": "^0.2.38",
"uuidv4": "^6.2.12"
},
"devDependencies": {
"#nestjs/cli": "^8.0.0",
"#nestjs/schematics": "^8.0.0",
"#nestjs/testing": "^8.0.0",
"#types/cookie-parser": "^1.4.2",
"#types/express": "^4.17.13",
"#types/jest": "^27.0.1",
"#types/node": "^16.0.0",
"#types/supertest": "^2.0.11",
"#typescript-eslint/eslint-plugin": "^4.28.2",
"#typescript-eslint/parser": "^4.28.2",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"supertest": "^6.1.3",
"ts-jest": "^27.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
I solved my problem. I messed up my imports/exports in some of the files of the project and after double-checking them it worked.
full error:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the CommitteesController (?).
Please make sure that the argument CommitteesService at index [0] is available in the CommitteesModule context.
Potential solutions:
- If CommitteesService is a provider, is it part of the current CommitteesModule?
- If CommitteesService is exported from a separate #Module, is that module imported within CommitteesModule?
#Module({
imports: [ /* the Module containing CommitteesService */ ]
})
at Injector.lookupComponentInParentModules (C:\Sites\AM-API-MDD\node_modules\#nestjs\core\injector\injector.js:193:19)
at async Injector.resolveComponentInstance (C:\Sites\AM-API-MDD\node_modules\#nestjs\core\injector\injector.js:149:33)
This was working with NestJS 7.6, but I upgraded the version to 8.0.1 and started getting this error.
There are several other modules that don't get an error, however, this is the first module in the list. The processor may not get to the other modules after returning this error.
committees.services.ts
import { Injectable } from '#nestjs/common';
import { PrismaService } from '../database';
import { defaultIncludeQuery, PER_PAGE_COUNT } from './constant';
import { Prisma } from '#prisma/client';
#Injectable()
export class CommitteesService {
constructor(private readonly prisma: PrismaService) {}
committees() {
return Promise.all([
this.prisma.committee.findMany({
include: {
PersonCommittee: {
include: {
Person: {
select: {
PKPersonID: true,
LastName: true,
FirstName: true,
MiddleName: true,
PreferredFirstName: true,
DisplayName: true,
EmploymentStatus: true
}
}
}
}
},
where: {
Active: true,
OR: [
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'A' } } } },
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'B' } } } },
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'C' } } } },
],
}
})
])
}
committees.controller.ts
import {
Body,Controller,DefaultValuePipe,Delete,Get,Param,ParseIntPipe,Post,Put,Query,
} from '#nestjs/common';
import { CommitteesService } from './committees.service';
import { RESOURCE_BASE_ROUTE } from '../constant';
import { PER_PAGE_COUNT } from './constant';
import { Prisma } from '#prisma/client';
const Route = RESOURCE_BASE_ROUTE.committees;
#Controller()
export class CommitteesController {
constructor(private readonly committeesService: CommitteesService) {}
#Get(`${Route}`)
all() {
return this.committeesService.committees();
}
#Get(`${Route}/:id`)
byId(#Param('id', ParseIntPipe) id: number) {
if ( id == -1 ) {
return this.committeesService.committees()
}
return this.committeesService.committee({
CommitteeID: id,
});
}
committees.modules.ts
import { Module } from '#nestjs/common';
import { CommitteesController } from './committees.controller';
import { CommitteesService } from './Committees.service';
#Module({
imports: [],
controllers: [CommitteesController],
providers: [CommitteesService],
exports: [ CommitteesService],
})
export class CommitteesModule {}
package.json
{
"name": "am-api-mdd",
"version": "2.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"#angular/router": "^6.1.10",
"#nestjs/common": "^8.0.10",
"#nestjs/core": "^8.0.6",
"#nestjs/microservices": "^7.6.18",
"#nestjs/platform-express": "^7.6.18",
"node-windows": "^1.0.0-beta.5",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^6.6.7"
},
"devDependencies": {
"#nestjs/cli": "^8.1.2",
"#nestjs/schematics": "^7.3.1",
"#nestjs/testing": "^7.6.18",
"#prisma/client": "^3.1.1",
"#types/express": "^4.17.13",
"#types/jest": "^26.0.24",
"#types/node": "^14.17.7",
"#types/supertest": "^2.0.11",
"#typescript-eslint/eslint-plugin": "^4.29.0",
"#typescript-eslint/parser": "^4.29.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^26.6.3",
"prettier": "^2.3.2",
"prisma": "^3.1.1",
"supertest": "^6.1.4",
"ts-jest": "^26.5.6",
"ts-loader": "^8.3.0",
"ts-node": "^9.0.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
I am trying to use import export statements in the sequelize migrations, have tried all the things in the other questions and github issues. Following are the files I am using to make it work.
.sequelizerc
require('#babel/register');
require('#babel/polyfill');
const path = require('path');
module.exports = {
'config': path.resolve('config', 'config.json'),
'models-path': path.resolve('models'),
'seeders-path': path.resolve('seeders'),
'migrations-path': path.resolve('migrations')
}
package.json
{
"name": "sideproject3",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "nodemon --exec babel-node --experimental-specifier-resolution=node ./bin/www.js",
"start": "node --experimental-specifier-resolution=node ./bin/www.js",
"lint": "eslint --ignore-path .gitignore \"**/*.js\"",
"lint-fix": "eslint --ignore-path .gitignore --fix \"**/*.js\""
},
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^8.2.0",
"express": "~4.16.1",
"express-jwt": "^6.0.0",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"jsonwebtoken": "^8.5.1",
"morgan": "~1.9.1",
"mysql2": "^2.2.5",
"sequelize": "^6.6.2"
},
"devDependencies": {
"#babel/cli": "^7.13.16",
"#babel/core": "^7.13.16",
"#babel/node": "^7.13.13",
"#babel/polyfill": "^7.12.1",
"#babel/preset-env": "^7.13.15",
"#babel/register": "^7.13.16",
"eslint": "^7.25.0",
"eslint-config-google": "^0.14.0",
"nodemon": "^2.0.7",
"sequelize-cli": "^6.2.0"
}
}
.babelrc
{
"presets": [
[
"#babel/preset-env"
]
]
}
migration.js
'use strict';
import { TABLE_NAMES, REQUEST_STATUS } from '../constants';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Requests', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
message: {
type: Sequelize.TEXT,
},
status: {
type: Sequelize.ENUM([Object.values(REQUEST_STATUS)]),
},
requested_time: {
type: Sequelize.DATE,
},
requested_by_id: {
type: Sequelize.INTEGER,
references: {
model: TABLE_NAMES.USERS,
key: 'id',
},
},
requested_to_id: {
type: Sequelize.INTEGER,
references: {
model: TABLE_NAMES.USERS,
key: 'id',
},
},
reject_response: {
type: Sequelize.TEXT,
},
response_time: {
type: Sequelize.DATE,
allowNull: true,
},
type: {
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Requests');
},
};
Solutions by others tell that using #babel/register and using preset-env should have worked, but it is not working and giving the following error
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module:
... require() of ES modules is not supported.