I'm trying to migrate an old express site to webpack, but don't wanna rework all the layout tempaltes which use express-handlebars. Is it possible to use express-handlebars with webpack?
handlebars-loader seems not to support the layout concept of express-handlebars, so is no help here.
A custom loader seems to do the trick:
express-handlebars-loader.js:
const loaderUtils = require('loader-utils');
const validateOptions = require('schema-utils');
const path = require('path');
const express = require('express');
const exphbs = require('express-handlebars');
module.exports = function (content) {
const options = loaderUtils.getOptions(this);
const app = options.app;
const contextCallback = options.contextCallback;
const view = path.relative(options.basePath, this.resourcePath);
const context = contextCallback(this.resourcePath, view);
var loaderAsyncCallback = this.async();
app.render(view, context, function (err, html) {
if (err) {
return loaderAsyncCallback(err);
}
const slug =
'// Module\n'
+ 'var code = ' + JSON.stringify(html) + ';\n'
+ '// Exports\n'
+ 'module.exports = code;'
loaderAsyncCallback(null, slug);
});
};
webpack.config.js:
const CopyPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const fs = require('fs');
const url = require('url');
const express = require('express');
const exphbs = require('express-handlebars');
const app = express();
// Handlebars Setup
/**
* Instantiate a Handlebars instance with our config (default layout, helpers, etc.)
*/
const handlebasInstance = exphbs.create({
defaultLayout: 'mainLayout',
// Specify helpers which are only registered on this instance.
helpers
});
app.engine('handlebars', handlebasInstance.engine);
app.set('view engine', 'handlebars');
app.use('/assets', express.static('assets'));
const basePath = path.resolve(__dirname, './views');
function generateHtmlPlugins(templateDir) {
const itemList = fs.readdirSync(templateDir);
return itemList.flatMap(item => {
const [ name, extension ] = item.split('.');
if (extension == 'handlebars') {
const templatePath = path.resolve(templateDir, item);
const outputPath = path.resolve(templateDir, name + '.html');
const outputName = path.relative(basePath, outputPath);
return new HtmlWebpackPlugin({
filename: outputName,
inject: false,
template: templatePath
})
} else {
return [];
}
})
}
const siteHtmlPlugins = generateHtmlPlugins(basePath);
function contextCallback(resourcePath, view) {
var context = {};
if (view.includes('documentation/')) {
context.layout = 'documentationLayout';
}
return context;
}
module.exports = {
mode: 'development',
resolveLoader: {
modules: [ 'node_modules', path.resolve(__dirname, 'loaders') ]
},
entry: './src/entry-workaround.js',
output: {
filename: 'entry-workaround.js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [{
test: /\.handlebars$/,
loader: "express-handlebars-loader",
options: {
app: app,
basePath: basePath,
contextCallback: contextCallback,
}
}]
},
plugins: []
};
Related
I'm developing a custom embedded app with Node+React.
I followed the official tutorial but if I use the verifyRequest() middleware I always get the following error when I navigate through my app's pages:
Expected a valid shop query parameter
I really can't understand what's wrong with the code.
Could anyone please help me?
Below is the server.js code
require('isomorphic-fetch');
const dotenv = require('dotenv');
const Koa = require('koa');
const next = require('next');
const { default: shopifyAuth } = require('#shopify/koa-shopify-auth');
const { verifyRequest } = require('#shopify/koa-shopify-auth');
const { default: Shopify, ApiVersion } = require('#shopify/shopify-api');
const Router = require('koa-router');
const RedisSessionStorage = require('./middleware/RedisSessionStorage')
const Cookies = require('cookies')
dotenv.config();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SHOPIFY_API_SCOPES.split(","),
HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.April21,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
RedisSessionStorage.storeCallback,
RedisSessionStorage.loadCallback,
RedisSessionStorage.deleteCallback,
),
});
const port = parseInt(process.env.PORT, 10) || 3001;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const ACTIVE_SHOPIFY_SHOPS = {[process.env.ACTIVE_SHOPIFY_SHOP]: process.env.SHOPIFY_API_SCOPES}
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
shopifyAuth({
accessMode: 'offline',
afterAuth(ctx) {
const { shop, scope, accessToken } = ctx.state.shopify;
global.accessToken = accessToken
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
ctx.redirect(`/?shop=${shop}`);
},
}),
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.get("(/_next/static/.*)", handleRequest);
router.get("/_next/webpack-hmr", handleRequest);
router.get("(.*)", verifyRequest({accessMode: 'offline'}), handleRequest);
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
Have you tried with the default SESSION_STORAGE?
It looks like it's directing to /auth
I'm working on some test for a very small vanilla js library, so I have this module
var otherModule = require('../module/mymodule');
var postscribe = require('postscribe');
var exports = module.exports = {};
var API_URL = URL;
exports.myFunction = function (arg1, arg2, arg3) {
if (arg1 && arg2) {
var myUrl = getApiUrl(arg1, arg2, arg3);
callSomeURL(myUrl);
}
}
function getApiUrl(arg1, arg2, arg3) {
var param1 = otherModule.getParams(arg1);
var param2 = otherModule.getOtherParams(arg1);
return `${API_URL}/v1/pixels/${arg1}/${arg2}${param1}${param2}`;
}
...
then I have otherModule module with my functions
var GLOBAL_VALUE = MY_VALUE;
var otherModule = {};
otherModule.getParams = function (arg1) {
return arg1 ? `&value=${arg1}` : '';
}
otherModule.getOtherParams = function (arg1) {
return GLOBAL_VALUE + arg1
}
module.exports = otherModule;
And my webpack configs
const { DefinePlugin } = require('webpack');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const devConfig = require('./src/config/dev');
module.exports = () => {
var envConfig = devConfig;
return merge(common, {
mode: 'development',
devtool: 'inline-source-map',
plugins: [
new DefinePlugin({
__DEV__: true,
URL: JSON.stringify(envConfig.URL),
MY_VALUE: JSON.stringify(envConfig.VALUE)
})
]
})
}
my common is:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new CleanWebpackPlugin()
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
And my jest.config
const config = require('./src/config/dev');
module.exports = {
globals: {
"MY_VALUE": config.VALUE,
"URL" : config.URL
}
}
my problem appears when I try to test exports.myFunction via rewire to have access to private function getApiUrl it seems that I can't access to the imported global values, like GLOBAL_VALUE
in my myOther, I keep getting ReferenceError: GLOBAL_VALUE is not defined, but when I test myOther module directly everything seems to work, can someone throw some light or resources on what I'm doing wrong?
I want to upload image on by using graphql-upload package
This is my scheme file
const { GraphQLSchema, GraphQLObjectType, GraphQLBoolean } = require("graphql");
const { GraphQLUpload } = require("graphql-upload");
const schema = new GraphQLSchema({
mutation: new GraphQLObjectType({
name: "Mutation",
fields() {
return {
uploadImage: {
description: "Uploads an image.",
type: GraphQLBoolean,
args: {
image: {
description: "Image file.",
type: GraphQLUpload
}
},
async resolve(parent, { image }) {
const { filename, mimetype, createReadStream } = await image;
const stream = createReadStream();
// Promisify the stream and store the file, then…
return true;
}
}
};
}
})
});
This is my server file. When I run the file it gives error on the browser side. it showed: {"errors":[{"message":"Must provide query string."}]}.
const express = require("express");
const graphqlHTTP = require("express-graphql");
const { graphqlUploadExpress } = require("graphql-upload");
const schema = require("./schema");
express()
.use(
"/graphql",
graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }),
graphqlHTTP({ schema })
)
.listen(3000);
**What am I doing wrong? **
I'm trying to call a simple sequelize raw query to get all the users in the users table but I am getting the error.
Cannot read property 'query' of undefined
what am I doing wrong here?
models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
sequelize = new Sequelize(
config.database, config.username, config.password, config
);
}
fs
.readdirSync(__dirname)
.filter(file =>
(file.indexOf('.') !== 0) &&
(file !== basename) &&
(file.slice(-3) === '.js'))
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
module.exports = db;
controllers/users
const db = require('../models/index').db;
module.exports = {
getAllUsers(req, res){
db.query('SELECT * FROM "Users" As Users', { type: sequelize.QueryTypes.SELECT})
.then(result => {
return res.status(201).send({
result
});
})
}
};
Had to change
const db = require('../models/index').db;
to
const db = require('../models/index');
and add const sequelize = require('sequelize');
I'm building a CRUD app using electron + vuejs + sequelize, i have used sequelize init and configured the .sequelizerc as below
const path = require('path');
module.exports = {
'config': path.resolve('src/renderer/database/config', 'config.json'),
'models-path': path.resolve('src/renderer/database', 'models'),
'seeders-path': path.resolve('src/renderer/database', 'seeders'),
'migrations-path': path.resolve('src/renderer/database', 'migrations')
}
Now i'm trying to populate a table with data from the database, I've tried to import the models in many ways, always leading to errors.
Error: Uncaught TypeError: Path must be a string. Received undefined
Component:
var models = require('./../../database/models'); // LINE WITH ERROR
export default {
name: "user-index",
data: function() {
return {
users: []
};
},
created: function() {
models.Users.findAll().then(users => {
//
});
},
components: {},
methods: {}
};
Thanks.
EDIT:
I found the problem, it is in the models/index.js:
'use strict';
var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var basename = path.basename(__filename);
var env = process.env.NODE_ENV || 'development';
var config = require(__dirname + '/../config/config.json')[env];
var db = {};
if (config.use_env_variable) {
var sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
var sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
var model = sequelize['import'](path.join(__dirname, file)); // THIS LINE
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
I fixed by changing path.join to path.resolve and changing this:
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
to
for (var modelName in db) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
}
in models/index.js file.