I'm working on an Excel Add-in using the OfficeJS API. This add-in is primarily shortcut-based, and every JS function has been tested using the Taskpane. I have followed all the documentation: https://learn.microsoft.com/en-us/office/dev/add-ins/design/keyboard-shortcuts, but they will not work. I have SharedRuntime 1.1 and KeyboardShortcuts 1.1 supported, and the SharedRuntime implemented in the Manifest and Webpack. I'll show my Manifest, Webpack, Shortcuts.json, and JS code below.
Manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp">
<Id>13f82994-aa90-4506-b154-c433c9c847cb</Id>
<Version>1.0.0.0</Version>
<ProviderName>Contoso</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="add_in_2"/>
<Description DefaultValue="A template to get started."/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/>
<SupportUrl DefaultValue="https://www.contoso.com/help"/>
<AppDomains>
<AppDomain>https://www.contoso.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Workbook"/>
</Hosts>
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="SharedRuntime" MinVersion="1.1"/>
</Sets>
</Requirements>
<DefaultSettings>
<SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Workbook">
<Runtimes>
<Runtime resid="Taskpane.Url" lifetime="long" />
</Runtimes>
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<FunctionFile resid="Taskpane.Url"/>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<OfficeTab id="TabHome">
<Group id="CommandsGroup">
<Label resid="CommandsGroup.Label"/>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Control xsi:type="Button" id="TaskpaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>ButtonId1</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/>
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/>
<bt:String id="CommandsGroup.Label" DefaultValue="Commands Group"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started."/>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
<ExtendedOverrides Url="https://localhost:3000/src/shortcuts.json"></ExtendedOverrides>
</OfficeApp>
Webpack:
/* eslint-disable no-undef */
const devCerts = require("office-addin-dev-certs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const urlDev = "https://localhost:3000/";
const urlProd = "https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION
async function getHttpsOptions() {
const httpsOptions = await devCerts.getHttpsServerOptions();
return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert };
}
module.exports = async (env, options) => {
const dev = options.mode === "development";
const config = {
devtool: "source-map",
entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
taskpane: "./src/taskpane/taskpane.js",
commands: "./src/commands/commands.js",
},
output: {
clean: true,
},
resolve: {
extensions: [".html", ".js"],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.html$/,
exclude: /node_modules/,
use: "html-loader",
},
{
test: /\.(png|jpg|jpeg|gif|ico)$/,
type: "asset/resource",
generator: {
filename: "assets/[name][ext][query]",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["polyfill", "taskpane"],
}),
new CopyWebpackPlugin({
patterns: [
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]" + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
}
},
},
],
}),
],
devServer: {
headers: {
"Access-Control-Allow-Origin": "*",
},
server: {
type: "https",
options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
},
port: process.env.npm_package_config_dev_server_port || 3000,
},
};
return config;
};
Shortcuts.json:
{
"actions": [
{
"id": "TOGGLENEGATIVE",
"name": "Toggle cells between Negative and Positive"
}
],
"shortcuts": [
{
"action": "TOGGLENEGATIVE",
"key": {
"default": "Ctrl+Shift+N"
}
}
]
}
JS Code:
Office.actions.associate("TOGGLENEGATIVE", toggleNegative);
export async function toggleNegative() {
Excel.run(async (context) => {
const rangeAreas = context.workbook.getSelectedRanges();
rangeAreas.areas.load("formulas");
await context.sync();
const rangeItems = rangeAreas.areas.items;
var newRangeItems = rangeItems;
for (let i = 0; i < rangeItems.length; i++) {
var range = rangeItems[i];
const newResults = range.formulas.map((r) => {
return r.map((c) => {
let form;
if (typeof c === "number") {
form = c * -1;
} else if (c.slice(0, 3) == "=-(") {
form = "=" + c.slice(3, c.length - 1);
} else if (c.slice(0, 1) == "=") {
form = "=-(" + c.slice(1, c.length) + ")";
}
return form;
});
});
newRangeItems[i].formulas = newResults;
}
});
}
Related
After using Webpack for lazy-loading (green coding) my vanilla.js client side router fails on advanced routes (.../blog/2 as one example) because the necessary .js file is blocked as described in the title. By using a button-link everything is fine ( data-btnref="navlink" ) but by direct enter the address in the browser it replies with the error.
GET http://localhost:3000/blog/main8f1360562bd664fccb25.js [HTTP/1.1 404 Not Found 3ms]
The resource from “http://localhost:3000/blog/main8f1360562bd664fccb25.js” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff).
Loading failed for the <script> with source “http://localhost:3000/blog/main8f1360562bd664fccb25.js”.
I know that this is an advanced question but maybe some professional coder likes my "self-learned" approach to handle things and help me out. I cleaned my approach as far as i can.
code:
index.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client side routing with vanilla.js + webpack lazy load</title>
</head>
<body>
<header>
<nav id="nav">
Home
Blog
Contact
</nav>
</header>
<main id="main"></main>
</body>
</html>
index.js:
import { navigateTo } from "./js/functionality/router.js";
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => {
switch (e.target.dataset.btnref) {
case ("navlink"):
e.preventDefault();
navigateTo(e.target.href, "navlink");
break;
default:
break;
};
});
navigateTo(location.pathname);
});
router.js:
const Home = () => import('../views/home');
const Blog = () => import('../views/blog');
const Blogpost = () => import('../views/blogpost');
const Contact = () => import('../views/contact');
const Login = () => import('../views/login');
const PageNotFound = () => import('../views/404');
export const navigateTo = (url, btnref) => {
switch (btnref) {
case ("navlink"):
const raw_url = url.split("/");
if (raw_url.length > 4) {
const corrected_url = "/blog/" + raw_url[raw_url.length - 1];
history.pushState(null, null, corrected_url);
} else {
history.pushState(null, null, url);
};
break;
default:
break;
};
router();
};
const router = async () => {
const routes = {
"/": Home,
"/blog": Blog,
"/blog/:id": Blogpost,
"/contact": Contact,
"/login": Login,
"/404": PageNotFound,
};
let match;
if (routes[location.pathname]) {
match = await routes[location.pathname]().then(m => { return m.default() });
} else {
if (location.pathname.split("/")[1] === "blog") {
const id = location.pathname.split("/")[2];
match = await routes["/blog/:id"]().then(m => { return m.default(id) });
} else {
match = await routes["/404"]().then(m => { return m.default(`${location.pathname}`) });
};
};
document.getElementById('main').innerHTML = match;
};
window.addEventListener("popstate", router);
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
mode: 'development',
entry: {
main: path.resolve(__dirname, 'src/index.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name][contenthash].js',
clean: true,
assetModuleFilename: '[name][ext]',
},
devtool: 'source-map',
devServer: {
static: {
directory: path.resolve(__dirname, 'dist'),
},
port: 3000,
open: true,
hot: true,
compress: true,
historyApiFallback: true
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(svg|png|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack App',
filename: 'index.html',
template: 'src/template.html',
}),
new BundleAnalyzerPlugin(),
]
}
The solution to this issue is to add <base href="/" /> inside the <head> of the template.html. This fixed the 404 - error:
The resource from “http://localhost:3000/blog/main8f1360562bd664fccb25.js” ....
to the correct path:
http://localhost:3000/main8f1360562bd664fccb25.js
This problem is also known as "Webpack chunks fail to load in nested routes #333 - GitHub".
I try to create react library with Webpack.
Simplified structure of library:
+ Base
|
|--- index.js
export * from "./Base";
|--- Base.jsx
export const Base = { "base_property_1": "base_property_1_value" };
+ Data
|
|--- index.js
export * from "./Data";
|--- Data.jsx
import { Base } from "../Base";
export const Data = { "data_property_1": "data_property_1_value", ...Base };
+ Red
|
|--- index.js
export * from "./Red";
|--- Red.jsx
import React from "react";
export const Red = () => <div>[Red]</div>;
I try to build library with this webpack.library_create.config.js:
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: {
"Base": path.resolve(__dirname, "./library_src/Base"),
"Data": path.resolve(__dirname, "./library_src/Data"),
"Red": path.resolve(__dirname, "./library_src/Red"),
},
externals: {
"react": "commonjs react",
"react-dom": "commonjs react-dom",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
optimization: {
minimize: false,
splitChunks: {
chunks: "all",
minChunks: 2,
minSize: 1,
},
},
output: {
clean: true,
filename: "[name]/index.js",
libraryTarget: "umd",
library: "Astra",
path: path.resolve(__dirname, "./node_modules/#a/library"),
umdNamedDefine: true,
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
target: "web",
};
Then I try to start project:
+ dist
|--- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
</head>
<body>
<div></div>
<script src="./bundle.js"></script>
</body>
</html>
+ library_use
|--- index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { Data } from "#a/library/Data";
import { Red } from "#a/library/Red";
console.log(Data);
const App = () => <div>App <Red /></div>;
ReactDOM.render( <App />, document.querySelectorAll( "body > div" )[ 0 ] );
using webpack.library_use.config.js:
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: path.resolve(__dirname, "./library_use/index.jsx"),
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
plugins: [ new webpack.HotModuleReplacementPlugin() ],
devServer: {
hot: true,
open: true,
port: 3020,
static: [
path.resolve(__dirname, "./dist"),
{
directory: path.resolve(__dirname, "./dist"),
serveIndex: true,
watch: true,
},
],
},
};
And I getting error: Uncaught TypeError: Cannot read properties of undefined (reading 'Data')
i.e. Data is not initialized.
If I remove dependency Base from Data, then Data initialized:
+ Data
|--- Data.js
// import { Base } from "../Base";
// export const Data = { "data_property_1": "data_property_1_value", ...Base };
export const Data = { "data_property_1": "data_property_1_value", };
How to set up webpack.library_create.config.js to build my library with dependencies to work my project (with webpack.library_use.config.js)?
This project on git https://github.com/rosinfotech/issue_210921_webpack_library_dependencies
Eventually, nobody answered my issue. Even collaborators of the Webpack projects define the issue as discussion and suppose to figure out in Webpack code to resolve this problem myself https://github.com/webpack/webpack/discussions/14303#discussioncomment-1376696 But I chosen another way – the Rollup, which resolved my task very effectively https://github.com/rosinfotech/issue_210921_webpack_library_dependencies/tree/rollup
I am trying to extract different scss entries to separate css bundle files
My folder structure goes something like:
src
themes
red.scss
blue.scss
index.js
red.scss and blue.scss are importing other (same) .scss files
index.js doesnt import any .scss
i want my output to be:
index.js or bundle.js
blue.css
red.css
i used minicssextractplugin, to do the job, but all i got was:
index.js
blue.js
red.js
Used many tutorials but most of them are compatible with webpack 4.
Used optimization, splitChunks, cacheGroups etc
All kind of different entry cases
My dependencies are:
"devDependencies": {
"#babel/core": "^7.12.10",
"#babel/plugin-proposal-object-rest-spread": "^7.12.1",
"#babel/preset-env": "^7.12.11",
"babel-loader": "^8.2.2",
"babel-polyfill": "^6.26.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"file-loader": "^6.2.0",
"mini-css-extract-plugin": "^1.3.3",
"node-sass": "^5.0.0",
"sass-loader": "^10.1.0",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.11.0",
"webpack-cli": "^4.2.0"
},
I cant copy my webpack.config.js in here, getting error about format
Here are some parts:
entry: {
red: path.resolve(__dirname, 'src/themes/red/'),
blue: path.resolve(__dirname, 'src/themes/blue/'),
},
//devtool: 'source-map',
//entry: './src/index.js',
//entry: ['./src/index.js', './src/css/index.scss'],
//entry: {
// //default: './src/default.js',
// blue: './src/themes/blue',
// red: './src/themes/red'
//},
//entry: {
// //index: './src/index.js',
// //main: './src/index.js',
// //styles: ['./src/themes/red.scss', './src/themes/default.scss', './src/themes/blue.scss']
// default: ['./src/default.js', './src/themes/default.scss'],
// red: ['./src/red.js', './src/themes/red.scss'],
// blue: ['./src/blue.js', './src/themes/blue.scss'],
//},
output: {
path: path.resolve(__dirname, 'wwwroot/dist'),
filename: '[name].js'
//sourceMapFilename: '[name].js.map'
},
optimization: {
splitChunks: {
cacheGroups: {
redStyles: {
name: 'styles_red',
test: (m, c, entry = 'red') =>
m.constructor.name === 'CssModule' &&
recursiveIssuer(m, c) === entry,
chunks: 'all',
enforce: true,
},
blueStyles: {
name: 'styles_blue',
test: (m, c, entry = 'blue') =>
m.constructor.name === 'CssModule' &&
recursiveIssuer(m, c) === entry,
chunks: 'all',
enforce: true,
},
},
},
},
module: {
rules: [
{
test: /\.s[c|a]ss$/,
include: path.resolve(__dirname, 'src'),
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
]
},
//{
// test: /\.js$/,
// include: path.resolve(__dirname, 'src'),
// loader: 'babel-loader',
// options: {
// presets: ["#babel/preset-env"],
// plugins: ['#babel/plugin-proposal-object-rest-spread']
// }
//},
{
test: /\.(png|jpg)$/,
include: path.resolve(__dirname, 'src'),
use: {
loader: "file-loader"
}
}
]
}
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
//new FixStyleOnlyEntriesPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css",
}),
//new MiniCssExtractPlugin({
// filename: "[name].css",
// //chunkFilename: "[name].css"
//})
//new MiniCssExtractPlugin({
// filename: '[name].css'
//}),
//defaultTheme,
//redTheme,
//blueTheme
]
(copy of my answer)
my case: webpack 5 + multipage application + themes.css via entry points
solution: https://github.com/webdiscus/webpack-remove-empty-scripts
this plugins don't work with webpack 5 entry points or with MiniCssExtractPlugin:
webpack-fix-style-only-entries,
webpack-extraneous-file-cleanup-plugin,
webpack-remove-empty-js-chunks-plugin,
webpack-delete-no-js-entries-plugin.
my webpack.config.js:
const fs = require('fs');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;
const PAGES = ['app', 'help'];
const getThemes = (themePath, alias) => {
let themes = {};
const longPath = './' + alias + '/' + themePath;
fs.readdirSync(longPath).forEach(function(fileName) {
const fileNameWithPath = path.join(themePath, fileName);
const fileNameWithLongPath = path.join(longPath, fileName);
const stat = fs.lstatSync(fileNameWithLongPath);
if (stat.isDirectory()) return;
if (!/\.scss$/.test(fileName)) return;
const nameWithoutExt = path.basename(fileName, '.scss');
themes[nameWithoutExt] = ['./' + fileNameWithPath];
});
console.log(themes);
return themes;
};
const themes = getThemes('scss/themes', 'src');
const getFilename = (filename, ext) => {
let name = filename == 'index' ? 'bundle' : filename;
const isTheme = (ext == 'css' && name.startsWith('theme')) ? true : false;
const needHash = (isDev || isTheme) ? false : true;
return needHash ? name +`.[fullhash].` + ext : name+'.'+ext;
};
const getCSSDirname = filename => {
const isTheme = filename.startsWith('theme');
return !isTheme? '/css/' : '/css/theme/';
};
const getHTMLWebpackPlugins = arr => {
// this function config multipages names and add to html-pages
// inside <head> tag our themes via tag <link rel="stylesheet" href="....css" ...>
// and return array of HTMLWebpackPlugins
};
module.exports = {
// ... //
entry: {
// mutipage:
app: ['./index.js', './scss/app.scss'],
help: ['./help.js', './scss/help.scss'],
// multitheme:
...themes,
},
optimization: {
removeEmptyChunks: true, // not work!!!
},
// ... //
plugins: [
// ... //
...getHTMLWebpackPlugins(PAGES),
new RemoveEmptyScriptsPlugin({
ignore: PAGES,
enabled: isDev === false,
}),
new MiniCssExtractPlugin({
filename: pathdata => {
return getCSSDirname(pathdata.chunk.name) + getFilename(pathdata.chunk.name, 'css');
},
chunkFilename: isDev ? '[id].css' : '[id].[contenthash].css',
}),
],
};
my src files:
[src]:
- index.js
- index.html
- help.js
- help.html
- [scss]:
- - app.scss
- - help.scss
- - [themes]:
- - - light.scss
- - - dark.scss
- - - blue.scss
after build:
[dist]:
- app.js
- index.html
- help$hash.js
- help$hash.html
- [css]:
- - app$hash.css
- - help$.css
- - [themes]:
- - - light.css
- - - dark.css
- - - blue.css
I have been given a chatbot project in javascript using webpack and for some reason I am unable to make it run succesfully! Web pack is running by starting the script with npm start but the page is blank.
Here is the index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Chatbot</title>
<script src="assets/lib/jquery.min.js"></script>
<script></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
this is index.js
import Dialogflow from './dialogflow';
import './assets/styles/main.css';
console.log("helloooo 2")
if (!$) {
console.error("Bot: This module requires jQuery.");
}
let default_messages = {
header: {
'en-US': 'Hi! How can I help you?',
},
inputPlaceholder: {
'fi': 'Kirjoita jotain (paina enter lähettääksesi)',
'en-US': 'Write something (press Enter to send)',
}
}
let lang = document.documentElement.lang;
class Bot{
renderHumanMessage(message) {
if (!message) {
return;
}
$('#sc-chb-response').append(`
<div class="sc-chb-conversationPlaceholder">
<div class="sc-chb-userRequest">
<div class="sc-chb-userReply">
<div class="sc-chb-chatBubbleHuman"></div>
` + message + `
</div>
</div>
<div class="sc-chb-usrImg">
<div class="sc-chb-responseIcon">
<div class="sc-chb-iconUserImg"></div>
</div>
</div>
</div>
`);
}
addOptionClickListener(chatBot) {
return function(e) {
$('#sc-chb-inputMessage').val($(this).attr('data-option'));
chatBot.sendMessage();
};
}
open() {
if (!this.hasToggleButton) {
$('#sc-chb-chat-box-toggle').hide();
} else {
$('.sc-chb-chatState').hide();
$('#sc-chb-chatStateClose').show();
}
$('#sc-chb-chat-box').show();
if (this.triggerWelcomeEvent === true) {
// Request the welcome message by triggering the welcome event
Dialogflow.triggerEvent('Welcome').then(res => {
this.renderBotMessage(res);
});
this.triggerWelcomeEvent = false;
}
}
toggle() {
if (this.isOpen()) {
this.close();
} else {
this.open();
}
}
isOpen() {
return $('#sc-chb-chat-box').is(":visible");
}
renderFulfillmentText(text) {
let processedText = text.replace(new RegExp('\n', 'g'), '<br>');
// renders [[text]] to a clickable chat bubble
processedText = processedText.replace(/\[\[([^\[\]]+)\]\](<br>)?/g, '<div class="sc-chb-chatbotOption" data-dialogflow="\$1" data-option="\$1">\$1</div>');
// renders {{{name|url}}} as name
return processedText.replace(/\{\{([^\{\}]+)\|([^\{\}]+)\}\}/g, '\$1');
}
}
export { Bot};
and there is an example.js file that shows how to use the bot class:
import { Bot} from './Bot';
let bot = new Bot({
"baseUrl": "xxxxx"
});
bot.init();
Webpack config:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const autoprefixer = require('autoprefixer');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {minimize: true}
}
]
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader",
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
}
]
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'url-loader?name=assets/[name].[hash].[ext]'
}
]
},
devServer: {
contentBase: 'src/',
// historyApiFallback: true,
// disableHostCheck: true,
port: process.env.PORT || 8080,
// host: '0.0.0.0',
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
require('autoprefixer'),
new MiniCssExtractPlugin({
filename: "main.css",
chunkFilename: "main.css"
}),
new CopyWebpackPlugin([
{ from: 'src/assets', to: 'assets' },
]),
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
library: 'sc-chatbot-frontend',
libraryTarget: 'umd'
},
externals: {
'jquery': 'jQuery',
'url-loader': 'url-loader'
}
};
I am not sure from where do I use this example.js or where does this needs to be defined? I tried to inlclude to index.html but still page is blank.
Basically i run npm start and a blank page appears, it should show the chatbot instead!
I'm new to universal rendering. I have a wildcard route in express that should catch anything that's not prepended with /api.
It looks like this:
module.exports = function(req, res){
const location = createLocation(req.url);
const store = configureStore();
match({routes, location}, (err, redirectLocation, renderProps) => {
if (err) {
console.error('Error!', err);
return res.status(500).end('Internal server error');
}
if (!renderProps) return res.status(404).end('Not found.');
const InitialComponent = (
<div style={{ width: '80%' }}>
<Provider store={store}>
<RoutingContext {...renderProps} />
</Provider>
</div>
);
const initialState = store.getState();
// | | | | | | | |
// v v v v running this will cause server to crash v v v v
const componentHTML = ReactDOMServer.renderToString(InitialComponent);
console.log('Component html? ', componentHTML);
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<base href="/" />
<title>rūh collective: server</title>
</head>
<body>
<div id="react-app">${html}</div>
<script src="bundle.js"></script>
</body>
</html>
`
res.send(html);
});
}
This successfully sends to the server, because I see that the title has the ruh:server included.
Right after the page loads, the server crashes saying pointing to events.js:82
If I comment out the ReactDOMServer.renderToString and I remove the reference in the html the server loads and does not crash.
If I run ReactDOMServer.renderToString and DO NOT EVEN REFERENCE IT, the server still crashes on load. Commenting out will allow the server to live.
Here is my webpack config:
var webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const commonLoaders = [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'react-hot',
}, {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
stage: 0,
}
},
{
test: /\.jsx$/,
loader: 'babel-loader',
query: {
stage: 0,
}
},
{ test: /\.css$/, loader: "style-loader!css-loader" },
{
test: /\.html$/,
loader: 'file?name=[name].[ext]',
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'file?hash=sha512&digest=hex&name=[hash].[ext]',
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false',
]
}
];
//const assetsPath = path.join(__dirname, 'dist');
const publicPath = path.join(__dirname, 'dist');
module.exports =
{
name: 'browser',
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./browser/js/main.js',
],
output: {
filename: 'bundle.js',
path: __dirname + '/dist',
publicPath: 'http://localhost:8080/',
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
],
module: {
loaders: commonLoaders.concat([
{test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
])
}
}
If I comment out the line const componentHTML etc, the server will run, but it's not rendering universally. I can try to replicate the issue if anyone is interested