How to run a fetch inside a Vite Plugin - vite

Am creating a vite plugin which will enable me to generate a sitemap for my website but am not finding how to make a fetch request using one of their many hooks. Am using simplecrawler module to run the fetch inside the plugin but it doesn't seem to work.
import type { Plugin, ResolvedConfig } from "vite";
import crawler from "simplecrawler";
const crawl = async (port:number): Promise<string[]> => {
return new Promise((resolve)=> {
const links: Set<string> = new Set();
const Crawler = new crawler(`http://localhost:${port}`);
Crawler.on("fetchcomplete", (queueItem, _responseBody, response) => {
if (response.headers["content-type"] === "text/html") {
links.add(queueItem.url);
}
});
Crawler.on("complete", () => {
const paths = [...links].map((link) => {
return new URL(link).pathname;
});
resolve(paths);
});
Crawler.start()
});
};
const plugin = ():Plugin =>{
const moduleId = `virtual:sitemap`;
const resolvedModuleId = `\0${moduleId}`;
let config: ResolvedConfig;
return {
name: "vite-plugin-svelte-sitemap",
resolveId: id => {
if (id === moduleId){
return resolvedModuleId
}
return id
},
configResolved: resolvedConfig => {
config = resolvedConfig;
},
buildStart: ()=>{
return async ()=>{
let port = config.server.port !== undefined ? config.server.port : (config.mode === "development" ? 5173 : 4173);
let urls = await crawl(port);
console.log(urls);
}
}
}
}
export default plugin;
So far, this is what I have been able to come up with

Related

State is null when trying to access it in the parent component, but able to pass it down to child component

EDIT: Simplified the code a bit by using two separate states. Adding the channel state to the dependency array of useEffect() does not remove the error but does make the application run in a strange manner: If I try to send messages, I see multiple messages.
EDIT 2: If I use the useRef() hook to store the state, my application works albeit in a strange manner again: the state is one step slower than the actual user input.
I'm trying to build a simple chat application using React.js and Socket.IO where a single user can communicate with multiple users. I have an endpoint from where I fetch all my channel data and store it in a state but I'm not able to access the state (returns null) even if the state is being passed down to a different component as props and that is being rendered correctly the first time. When I click on a channel to select it, the channel list is again reset to null and I get this error:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'forEach')
I'm trying to implement this by following the tutorial from here. It's outdated, so I'm implementing a newer version of it. But even then, if I use the component class defined in the author's github, it seems to work just fine. I have not worked with component classes before, so I am not sure where I am going wrong.
Main component: Chat.js
import React, { useEffect, useState } from 'react';
import ChannelList from './ChannelList';
import './chat.css';
import MessagePanel from './MessagePanel';
import { io } from "socket.io-client";
const SERVER = "http://localhost:8080";
let socket;
function Chat() {
const [channel, setChannel] = useState(null)
const [channels, setChannels] = useState(null) //the state where the channels are loaded
const channelsRef = useRef() //edit 2
useEffect(() => {
loadChannels() //the function which loads the channels into the state
configureSocket()
}, []) //adding channel to the dependency array results in strange behavior.
const configureSocket = () => {
socket = io(SERVER)
socket.on('connection', () => {
if(channel) {
handleChannelSelect(channel.id)
}
});
socket.on('channel', channel => {
console.log('channel details', channel); //returns the correct channel clicked on
//let temp_channels = channels; //edit 2
let temp_channels = channelsRef.current; //edit 2
console.log('channels inside onchannel', temp_channels); //returns null, does not return null if useRef() is used
temp_channels.forEach(c => { //I get the above mentioned error at this line, after edit 2 I don't get any error
if(c.id === channel.id) {
c.participants = channel.participants
}
})
//setChannels(channels); edit 2
channelsRef.current = temp_channels // edit 2
})
socket.on('message', message => {
// let temp_channels = channels
let temp_channels = channelsRef.current //edit 2
temp_channels.forEach(c => {
if (c.id === message.channel_id) {
if (!c.messages) {
c.messages = [message];
} else {
c.messages.push(message);
}
}
})
// setChannels(channels) edit 2
channelsRef.current = temp_channels // edit 2
})
}
const handleSendMessage = (channel_id, text) => {
socket.emit('send-message', { channel_id, text, senderName: socket.id, id: Date.now()})
}
const handleChannelSelect = (id) => {
const channel = channels.find(c => {
return c.id === id;
});
setChannel(channel);
socket.emit('channel-join', id, ack => {});
}
const loadChannels = async () => {
await fetch('http://localhost:8080/getChannels')
.then(async response => {
const data = await response.json()
console.log('data inside loadchannels', data.channels);
setChannels(data.channels); //this sets the channels and passes it down to the child component "ChannelList"
channelsRef.current = data.channels //if setChannels is not used before this, it doesn't work
})
console.log('after fetching channels inside loadchannels', channel, channels); //both channel and channels state are null
}
return (
<div className="chat-app">
<ChannelList channels={channels} onSelectChannel={handleChannelSelect}></ChannelList>
<MessagePanel onSendMessage={handleSendMessage} channel={channel}/>
</div>
);
}
export default Chat;
ChannelList.js
import React from 'react';
import Channel from './Channel';
function ChannelList(props) {
const handleClick = (id) => {
props.onSelectChannel(id);
}
console.log('inside channellist component', props.channels); //returns the set of channels received from props
let list = <div className="no-content-message">There are no channels to show</div>;
if (props.channels && props.channels.map) {
list = props.channels.map(c =>
<Channel
key={c.id}
id={c.id}
name={c.name}
participants={c.participants}
onClick={handleClick}
/>
);
}
return (
<div className='channel-list'>
{list}
</div>
);
}
export default ChannelList;
Backend index.js:
const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io');
app.use(cors());
const server = http.createServer(app);
const STATIC_CHANNELS = [
{
name: 'Global chat',
participants: 0,
id: 1,
sockets: []
},
{
name: 'Funny',
participants: 0,
id: 2,
sockets: []
},
{
name: 'Test',
participants: 0,
id: 3,
sockets: []
},
];
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
});
server.listen(8080, () =>
console.log('Server is running on port 8080')
);
io.on('connection', (socket) => {
console.log(`User connected ${socket.id}`);
socket.emit('connected', socket.id);
socket.on('channel-join', id => {
console.log('channel join', id);
STATIC_CHANNELS.forEach(c => {
if (c.id === id) {
if (c.sockets.indexOf(socket.id) == (-1)) {
c.sockets.push(socket.id);
c.participants++;
io.emit('channel', c);
}
} else {
let index = c.sockets.indexOf(socket.id);
if (index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
}
});
return id;
})
socket.on('send-message', message => {
io.emit('message', message);
})
socket.on('disconnect', () => {
STATIC_CHANNELS.forEach(c => {
let index = c.sockets.indexOf(socket.id);
if(index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
})
})
});
app.get('/getChannels', (req, res) => {
res.json({
channels: STATIC_CHANNELS
})
})

Rxdb sync not update db

There are 3 bases (front, node, remote). Front <=> node, node <=> remote. When the front base is updated, the data goes to the remote base, but the node is not updated. In theory, the node should be updated first, and then the remote base.
Render db
addPouchPlugin(PouchdbAdapterIdb)
addPouchPlugin(PouchHttpPlugin)
addRxPlugin(RxDBReplicationCouchDBPlugin)
addRxPlugin(RxDBMigrationPlugin)
addRxPlugin(RxDBLeaderElectionPlugin)
addRxPlugin(RxDBQueryBuilderPlugin)
addRxPlugin(RxDBAjvValidatePlugin)
addRxPlugin(RxDBUpdatePlugin)
export const createDb = async () => {
console.log('[src/renderer/database/createDb] createDb')
const productsName = collectionName.getCollectionProductsName()
const documentsName = collectionName.getCollectionDocumentsName()
const settingsName = collectionName.getCollectionSettingsName()
const db = await createRxDatabase<Collections>({
name: 'renderer',
// use pouchdb with the indexeddb-adapter as storage engine.
storage: getRxStoragePouch('idb'),
})
await initCommonCollections({ db, documentsName, productsName, settingsName })
syncDbCollections(db, [productsName, documentsName, settingsName])
db.$.subscribe(({ operation, documentId, documentData }) => {
if (documentData.type === SettingsTypes.DEVICE_SETTING) {
console.log(`Change database RENDER event:\n ${operation}, \n documentData:`, documentData)
}
})
return db
}
Render sync
const remoteDbUrl = `http://localhost:3030/db/`
const logPath = '[src/renderer/database/syncDbCollections]'
export const syncDbCollections = (db: RxDatabase<Collections>, collectionNames: (keyof Collections)[]) => {
console.log('syncDbCollections', collectionNames)
collectionNames.forEach(name => {
const rxReplicationState = db.collections[name].syncCouchDB({
remote: `${remoteDbUrl}${name}`,
options: {
live: true,
retry: true,
},
})
rxReplicationState.error$.subscribe(error => {
console.error(logPath, name, 'error', JSON.stringify(error))
})
})
}
Node base
addPouchPlugin(PouchdbAdapterHttp)
addPouchPlugin(LevelDbAdapter)
addRxPlugin(RxDBAjvValidatePlugin)
addRxPlugin(RxDBMigrationPlugin)
addRxPlugin(RxDBServerPlugin)
addRxPlugin(RxDBLeaderElectionPlugin)
addRxPlugin(RxDBQueryBuilderPlugin)
addRxPlugin(RxDBUpdatePlugin)
addRxPlugin(RxDBReplicationCouchDBPlugin)
let db: RxDatabase<Collections>
export const getMainDb = () => {
if (!db) {
throw new Error('No available database.')
}
return db
}
export const getDocumentCollection = (): DocumentsRxCol => {
return db[collectionNames.getCollectionDocumentsName()]
}
export const getSettingsCollection = (): SettingsRxCol => {
return db[collectionNames.getCollectionSettingsName()]
}
export const getProductsCollection = (): ProductsRxCol => {
return db[collectionNames.getCollectionProductsName()]
}
export const initDatabase = async () => {
console.log(logPathAlias, 'initDatabase')
if (db) {
console.warn(logPathAlias, 'db instance already created!')
return db
}
db = await createRxDatabase<Collections>({
name: `${electronApp.getPath('userData')}/db`,
storage: getRxStoragePouch(LevelDown),
})
const productsName = collectionNames.getCollectionProductsName()
const documentsName = collectionNames.getCollectionDocumentsName()
const settingsName = collectionNames.getCollectionSettingsName()
await initCommonCollections({ db, productsName, documentsName, settingsName })
await syncCollections([productsName, documentsName, settingsName])
db.$.subscribe(({ operation, documentId, documentData }) => {
// if (documentData.type === SettingsTypes.DEVICE_SETTING) {
console.log(`Change database NODE event:\n ${operation}, \n documentData:`, documentData)
// }
})
const { app } = await db.server({
startServer: false, // (optional), start express server
// options of the pouchdb express server
cors: false,
pouchdbExpressOptions: {
inMemoryConfig: true, // do not write a config.json
logPath: `${electronApp.getPath('temp')}/rxdb-server.log`, // save logs in tmp folder
},
})
return app
}
const lastRetryTime = {}
const syncCollections = async (collections: CollectionNames[]) => {
collections.map(collectionName => {
const rxReplicationState = db.collections[collectionName].syncCouchDB({
remote: `${CouchDbServerUrl}/${collectionName}`,
options: {
live: true,
retry: true,
// #ts-ignore
// headers: {
// Authorization: `Bearer ${getAccessToken()}`,
// },
},
})
rxReplicationState.error$.subscribe(async error => {
console.error(logPathAlias, collectionName, String(error))
if (error.status === 401 && dayjs().diff(lastRetryTime[collectionName], 'seconds') > 10 && getIsRefreshFresh()) {
lastRetryTime[collectionName] = dayjs()
await rxReplicationState.cancel()
await refreshTokens()
await syncCollections([collectionName])
}
})
})
}
No errors
Moreover, if you save data in a remote database, then they are synchronized with the node
Help me :(

How to import plugins in Cypress 9 with Node 16?

I'm struggling trying to import external libraries to the plugin file.
If I do
const clipboardy = require('clipboardy')
it says "Error [ERR_REQUIRE_ESM]: require() of ES Module /[...]/e2e/node_modules/clipboardy/index.js from /[...]/e2e/cypress/plugins/index.js not supported.". I tried also with
import clipboardy from 'clipboardy'
but this is still not working. I really don't understand how to solve.Can you please help me? I'm on node 16.10 with cypress 9.0.0
Thanks in advance
EDIT: the plugin file
const clipboardy = require("clipboardy");
const csv = require("node-xlsx").default;
const fs = require("fs");
const { lighthouse, pa11y, prepareAudit } = require("cypress-audit");
module.exports = (on, config) => {
on("task", {
parseXlsx({ filePath }) {
return new Promise((resolve, reject) => {
try {
const jsonData = csv.parse(fs.readFileSync(filePath));
resolve(jsonData);
} catch (e) {
reject(e);
}
});
},
getClipboard() {
return clipboardy.readSync();
},
lighthouse: lighthouse(lighthouseReport => {
const categories = lighthouseReport.lhr.categories;
const audits = lighthouseReport.lhr.audits;
const formattedAudit = Object.keys(audits).reduce(
(metrics, curr) => ({
...metrics,
[curr]: audits[curr].numericValue
}),
{}
);
const formattedCategories = Object.keys(categories).reduce(
(metrics, curr) => ({
...metrics,
[curr]: categories[curr].score * 100
}),
{}
);
const results = {
url: lighthouseReport.lhr.requestedUrl,
...formattedCategories
};
console.log("Lighthouse results: [");
console.log(results);
console.log("]");
// fs.writeJSONSync("../results/audit.json", results);
// fs.writeFileSync(
// "audit.json",
// Buffer.from(JSON.stringify(results), "utf8")
// );
}),
pa11y: pa11y(pa11yReport => {
console.log("pa11y results: [");
console.log(pa11yReport);
console.log("]");
})
});
on("before:browser:launch", (browser, launchOptions) => {
prepareAudit(launchOptions);
if (browser.name === "chromium") {
launchOptions.args.push(
"--disable-features=CrossSiteDocumentBlockingIfIsolating,CrossSiteDocumentBlockingAlways,IsolateOrigins,site-per-process"
);
launchOptions.args.push(
"--load-extension=cypress/extensions/Ignore-X-Frame-headers_v1.1"
);
launchOptions.args.push("--disable-dev-shm-usage");
return launchOptions;
}
return launchOptions;
});
return config;
};
The problem was related ti clipboardy, which in version 3 became a ES Only modulo, not compatibile with Cypress. Downgrading to v2.3 solved the issue

Importing a module, error with library functions

i'm currently using NodeJS.
I'm trying to import a module to a component function and everything executes pretty well, but i still get this error in the server console:
error - src\modules\accountFunctions.js (15:35) # Object.User.GetData
TypeError: _cookieCutter.default.get is not a function
cookieCutter.get is actually a function and is working as inteneded
import cookieCutter from 'cookie-cutter'
import { useSelector, useDispatch } from 'react-redux'
import { useRouter } from 'next/router'
import { accountActions } from '../store/account'
const Auth = require('./auth.module')
const User = {}
User.GetData = async () => {
const route = useRouter()
const userData = useSelector((state) => state.user)
const dispatch = useDispatch()
const sessionId = cookieCutter.get('session')
if (sessionId && userData.username === '') {
const userExist = await Auth.loadUserInformation()
if (userExist.result === false) {
route.push('/login')
return false
}
dispatch(accountActions.updateAccountInformation(userExist.data))
return true
} else if (!sessionId) {
route.push('/login')
return false
}
}
module.exports = User
I know for a fact that a solution would be importing the library into the function compoenent but i really don't wanna keep on importing it everywhere.
This is how i'm importing the module.
import User from '../src/modules/accountFunctions'
const dashboard = () => {
console.log('Rance')
User.GetData()
return <NavBar />
}
export default dashboard
You need to move the cookie fetching logic to a useEffect inside the custom hook, so it only runs on the client-side. Calling cookieCutter.get won't work when Next.js pre-renders the page on the server.
const useUserData = async () => {
const route = useRouter()
const userData = useSelector((state) => state.user)
const dispatch = useDispatch()
useEffect(() => {
const getAuthenticatedUser = async () => {
const sessionId = cookieCutter.get('session')
if (sessionId && userData.username === '') {
const userExist = await Auth.loadUserInformation()
if (userExist.result === false) {
route.push('/login')
}
dispatch(accountActions.updateAccountInformation(userExist.data))
} else if (!sessionId) {
route.push('/login')
}
}
getAuthenticatedUser()
}, [])
}

How to send data from react editor to server?

I am trying to create an editor to update my backend data but I am stuck at sending data from client to backend
Here is my following front-end code:
import React, { useState } from "react";
import dynamic from "next/dynamic";
import { convertToRaw, EditorState, getDefaultKeyBinding } from "draft-js";
import draftToHtml from "draftjs-to-html";
const Editor = dynamic(
() => import("react-draft-wysiwyg").then((mod) => mod.Editor),
{ ssr: false }
);
const Missions = ({ teamData, editable }) => {
const { title, mission, teamId } = teamData;
const classes = useStyle();
const [missionContent, setMissionContent] = useState(mission);
const [editing, setEditing] = useState(false);
const [editorState, updateEditorState] = useState(EditorState.createEmpty());
const onEditorStateChange = (editData) => {
updateEditorState(editData);
};
const handleSave = async () => {
const selection = editorState.getSelection();
const key = selection.getAnchorKey();
const content = editorState.getCurrentContent();
const block = content.getBlockForKey(key);
const type = block.getType();
if (type !== "unordered-list-item" && type !== "ordered-list-item") {
if (
editorState.getCurrentContent().getPlainText("").trim().length !== 0
) {
const content = editorState?.getCurrentContent();
let html = await draftToHtml(convertToRaw(content));
await updateEditorState(EditorState.createEmpty(""));
setMissionContent(html.trim());
}
}
setEditing(false);
};
return (
<div className="team-mission-editor-container">
<Editor
wrapperClassName={"mission-editor-wapper"}
toolbarClassName={"mission-editor-toolbar"}
editorClassName={"mission-editor-editor"}
editorState={editorState}
onEditorStateChange={onEditorStateChange}
toolbar={{...}}
/>
)
Here is my back-end router:
router.put(
"/team/:teamId",
restrictedRoute,
checkData,
catchErrors(checkTeamPermissions),
catchErrors(updateTeamData)
);
and here is my update function from backend:
exports.updateTeamData = async (req, res) => {
// Get userId
const userId = req.session.passport.user.id;
// Get teamId
const publicTeamId = req.params.teamId;
// Fetch private id for team
const teamId = await getTeamId(publicTeamId);
// The user making the request
const userPublicId = req.session.passport.user.publicId;
// The creator of the team
const creatorPublicId = req.body.creator;
// Check who is making the request
if (userPublicId !== creatorPublicId) {
res.status(401).json("msg: You cant update a team you did not create");
}
// Updates
const payload = {
title: req.body.title,
mission: req.body.mission,
inputs: req.body.inputs,
outputs: req.body.outputs,
duration_in_months: req.body.duration_in_months,
status: req.body.status,
mergedTo: teamId,
};
// Update team data
await models.Team.update(payload, {
where: {
id: teamId,
creatorId: userId,
},
});
res.status(200).json("msg: Updated team successfully");
};
How can I send data fromo my editor to backend and update it?
Thank you so much for helping me

Resources