From be00a1c01372861f6754a9639a5bd6770a46b82a Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Wed, 20 Dec 2017 13:56:14 -0700
Subject: [PATCH] Support for using yarn instead of npm, include unread counts
on cold load (#6179)
* Close #6178
* Support for package managers besides npm
- Also fixes issue where upgrade-plugins wouldn't work
---
public/src/client/footer.js | 1 +
src/cli/index.js | 13 +++++---
src/{meta => cli}/package-install.js | 18 +++++++++--
src/cli/upgrade-plugins.js | 26 +++++++++------
src/cli/upgrade.js | 4 +--
src/middleware/header.js | 47 +++++++++++++++++++++++++++-
src/navigation/index.js | 11 ++++---
src/plugins/install.js | 29 ++++++++++++++---
8 files changed, 121 insertions(+), 28 deletions(-)
rename src/{meta => cli}/package-install.js (86%)
diff --git a/public/src/client/footer.js b/public/src/client/footer.js
index 7dcdade78b..7bc187921e 100644
--- a/public/src/client/footer.js
+++ b/public/src/client/footer.js
@@ -75,6 +75,7 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu
socket.on('event:new_post', onNewPost);
}
+ // DEPRECATED: remove in 1.8.0
if (app.user.uid) {
socket.emit('user.getUnreadCounts', function (err, data) {
if (err) {
diff --git a/src/cli/index.js b/src/cli/index.js
index 0bc95a7c6d..aa7ef2c257 100644
--- a/src/cli/index.js
+++ b/src/cli/index.js
@@ -3,7 +3,7 @@
var fs = require('fs');
var path = require('path');
-var packageInstall = require('../meta/package-install');
+var packageInstall = require('./package-install');
var dirname = require('./paths').baseDir;
// check to make sure dependencies are installed
@@ -12,12 +12,17 @@ try {
} catch (e) {
if (e.code === 'ENOENT') {
console.warn('package.json not found.');
- console.log('Populating package.json...\n');
+ console.log('Populating package.json...');
packageInstall.updatePackageFile();
packageInstall.preserveExtraneousPlugins();
- console.log('OK'.green + '\n'.reset);
+ try {
+ require('colors');
+ console.log('OK'.green);
+ } catch (e) {
+ console.log('OK');
+ }
} else {
throw e;
}
@@ -33,7 +38,7 @@ try {
console.warn('Dependencies not yet installed.');
console.log('Installing them now...\n');
- packageInstall.npmInstallProduction();
+ packageInstall.installAll();
require('colors');
console.log('OK'.green + '\n'.reset);
diff --git a/src/meta/package-install.js b/src/cli/package-install.js
similarity index 86%
rename from src/meta/package-install.js
rename to src/cli/package-install.js
index 4dba482b70..5f6f9917a5 100644
--- a/src/meta/package-install.js
+++ b/src/cli/package-install.js
@@ -29,15 +29,27 @@ function updatePackageFile() {
exports.updatePackageFile = updatePackageFile;
-function npmInstallProduction() {
+function installAll() {
process.stdout.write('\n');
- cproc.execSync('npm i --production', {
+
+ var prod = global.env !== 'development';
+ var command = 'npm install';
+ try {
+ var packageManager = require('nconf').get('package_manager');
+ if (packageManager === 'yarn') {
+ command = 'yarn';
+ }
+ } catch (e) {
+ // ignore
+ }
+
+ cproc.execSync(command + (prod ? ' --production' : ''), {
cwd: path.join(__dirname, '../../'),
stdio: [0, 1, 2],
});
}
-exports.npmInstallProduction = npmInstallProduction;
+exports.installAll = installAll;
function preserveExtraneousPlugins() {
// Skip if `node_modules/` is not found or inaccessible
diff --git a/src/cli/upgrade-plugins.js b/src/cli/upgrade-plugins.js
index e67f634f31..3be00cb5d1 100644
--- a/src/cli/upgrade-plugins.js
+++ b/src/cli/upgrade-plugins.js
@@ -7,9 +7,18 @@ var cproc = require('child_process');
var semver = require('semver');
var fs = require('fs');
var path = require('path');
+var nconf = require('nconf');
var paths = require('./paths');
+var packageManager = nconf.get('package_manager');
+var packageManagerExecutable = packageManager === 'yarn' ? 'yarn' : 'npm';
+var packageManagerInstallArgs = packageManager === 'yarn' ? ['add'] : ['install', '--save'];
+
+if (process.platform === 'win32') {
+ packageManagerExecutable += '.cmd';
+}
+
var dirname = paths.baseDir;
function getModuleVersions(modules, callback) {
@@ -85,7 +94,7 @@ function getInstalledPlugins(callback) {
}
function getCurrentVersion(callback) {
- fs.readFile(path.join(dirname, 'package.json'), { encoding: 'utf-8' }, function (err, pkg) {
+ fs.readFile(path.join(dirname, 'install/package.json'), { encoding: 'utf-8' }, function (err, pkg) {
if (err) {
return callback(err);
}
@@ -106,8 +115,8 @@ function checkPlugins(standalone, callback) {
async.waterfall([
async.apply(async.parallel, {
- plugins: async.apply(getInstalledPlugins),
- version: async.apply(getCurrentVersion),
+ plugins: getInstalledPlugins,
+ version: getCurrentVersion,
}),
function (payload, next) {
var toCheck = Object.keys(payload.plugins);
@@ -194,13 +203,12 @@ function upgradePlugins(callback) {
if (['y', 'Y', 'yes', 'YES'].indexOf(result.upgrade) !== -1) {
console.log('\nUpgrading packages...');
- var args = ['i'];
- found.forEach(function (suggestObj) {
- args.push(suggestObj.name + '@' + suggestObj.suggested);
- });
+ var args = packageManagerInstallArgs.concat(found.map(function (suggestObj) {
+ return suggestObj.name + '@' + suggestObj.suggested;
+ }));
- cproc.execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', args, { stdio: 'ignore' }, function (err) {
- callback(err, true);
+ cproc.execFile(packageManagerExecutable, args, { stdio: 'ignore' }, function (err) {
+ callback(err, false);
});
} else {
console.log('Package upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade -p'.green + '".'.reset);
diff --git a/src/cli/upgrade.js b/src/cli/upgrade.js
index 783681bb10..e5ab2b6c0c 100644
--- a/src/cli/upgrade.js
+++ b/src/cli/upgrade.js
@@ -3,7 +3,7 @@
var async = require('async');
var nconf = require('nconf');
-var packageInstall = require('../meta/package-install');
+var packageInstall = require('./package-install');
var upgrade = require('../upgrade');
var build = require('../meta/build');
var db = require('../database');
@@ -22,7 +22,7 @@ var steps = {
install: {
message: 'Bringing base dependencies up to date...',
handler: function (next) {
- packageInstall.npmInstallProduction();
+ packageInstall.installAll();
next();
},
},
diff --git a/src/middleware/header.js b/src/middleware/header.js
index 3824ff6fc3..5a896fcdd7 100644
--- a/src/middleware/header.js
+++ b/src/middleware/header.js
@@ -6,6 +6,8 @@ var jsesc = require('jsesc');
var db = require('../database');
var user = require('../user');
+var topics = require('../topics');
+var messaging = require('../messaging');
var meta = require('../meta');
var plugins = require('../plugins');
var navigation = require('../navigation');
@@ -109,10 +111,16 @@ module.exports = function (middleware) {
next(null, translated);
});
},
- navigation: async.apply(navigation.get),
+ navigation: navigation.get,
tags: async.apply(meta.tags.parse, req, data, res.locals.metaTags, res.locals.linkTags),
banned: async.apply(user.isBanned, req.uid),
banReason: async.apply(user.getBannedReason, req.uid),
+
+ unreadTopicCount: async.apply(topics.getTotalUnread, req.uid),
+ unreadNewTopicCount: async.apply(topics.getTotalUnread, req.uid, 'new'),
+ unreadWatchedTopicCount: async.apply(topics.getTotalUnread, req.uid, 'watched'),
+ unreadChatCount: async.apply(messaging.getUnreadCount, req.uid),
+ unreadNotificationCount: async.apply(user.notifications.getUnreadCount, req.uid),
}, next);
},
function (results, next) {
@@ -131,8 +139,45 @@ module.exports = function (middleware) {
setBootswatchCSS(templateValues, res.locals.config);
+ var unreadCount = {
+ topic: results.unreadTopicCount || 0,
+ newTopic: results.unreadNewTopicCount || 0,
+ watchedTopic: results.unreadWatchedTopicCount || 0,
+ chat: results.unreadChatCount || 0,
+ notification: results.unreadNotificationCount || 0,
+ };
+ Object.keys(unreadCount).forEach(function (key) {
+ if (unreadCount[key] > 99) {
+ unreadCount[key] = '99+';
+ }
+ });
+
+ results.navigation = results.navigation.map(function (item) {
+ if (item.originalRoute === '/unread' && results.unreadTopicCount > 0) {
+ return Object.assign({}, item, {
+ content: unreadCount.topic,
+ iconClass: item.iconClass + ' unread-count',
+ });
+ }
+ if (item.originalRoute === '/unread/new' && results.unreadNewTopicCount > 0) {
+ return Object.assign({}, item, {
+ content: unreadCount.newTopic,
+ iconClass: item.iconClass + ' unread-count',
+ });
+ }
+ if (item.originalRoute === '/unread/watched' && results.unreadWatchedTopicCount > 0) {
+ return Object.assign({}, item, {
+ content: unreadCount.watchedTopic,
+ iconClass: item.iconClass + ' unread-count',
+ });
+ }
+
+ return item;
+ });
+
templateValues.browserTitle = results.browserTitle;
templateValues.navigation = results.navigation;
+ templateValues.unreadCount = unreadCount;
templateValues.metaTags = results.tags.meta;
templateValues.linkTags = results.tags.link;
templateValues.isAdmin = results.user.isAdmin;
diff --git a/src/navigation/index.js b/src/navigation/index.js
index 9aec34dd25..0712ce79f5 100644
--- a/src/navigation/index.js
+++ b/src/navigation/index.js
@@ -19,15 +19,16 @@ navigation.get = function (callback) {
data = data.filter(function (item) {
return item && item.enabled;
}).map(function (item) {
+ item.originalRoute = item.route;
+
if (!item.route.startsWith('http')) {
item.route = nconf.get('relative_path') + item.route;
}
- for (var i in item) {
- if (item.hasOwnProperty(i)) {
- item[i] = translator.unescape(item[i]);
- }
- }
+ Object.keys(item).forEach(function (key) {
+ item[key] = translator.unescape(item[key]);
+ });
+
return item;
});
diff --git a/src/plugins/install.js b/src/plugins/install.js
index 7bd407ca08..da03fd8d71 100644
--- a/src/plugins/install.js
+++ b/src/plugins/install.js
@@ -13,6 +13,23 @@ var meta = require('../meta');
var pubsub = require('../pubsub');
var events = require('../events');
+var packageManager = nconf.get('package_manager') === 'yarn' ? 'yarn' : 'npm';
+var packageManagerExecutable = packageManager;
+var packageManagerCommands = {
+ yarn: {
+ install: 'add',
+ uninstall: 'remove',
+ },
+ npm: {
+ install: 'install',
+ uninstall: 'uninstall',
+ },
+};
+
+if (process.platform === 'win32') {
+ packageManagerExecutable += '.cmd';
+}
+
module.exports = function (Plugins) {
if (nconf.get('isPrimary') === 'true') {
pubsub.on('plugins:toggleInstall', function (data) {
@@ -95,7 +112,7 @@ module.exports = function (Plugins) {
setImmediate(next);
},
function (next) {
- runNpmCommand(type, id, version || 'latest', next);
+ runPackageManagerCommand(type, id, version || 'latest', next);
},
function (next) {
Plugins.get(id, next);
@@ -107,8 +124,12 @@ module.exports = function (Plugins) {
], callback);
}
- function runNpmCommand(command, pkgName, version, callback) {
- cproc.execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', [command, pkgName + (command === 'install' ? '@' + version : ''), '--save'], function (err, stdout) {
+ function runPackageManagerCommand(command, pkgName, version, callback) {
+ cproc.execFile(packageManagerExecutable, [
+ packageManagerCommands[packageManager][command],
+ pkgName + (command === 'install' ? '@' + version : ''),
+ '--save',
+ ], function (err, stdout) {
if (err) {
return callback(err);
}
@@ -125,7 +146,7 @@ module.exports = function (Plugins) {
function upgrade(id, version, callback) {
async.waterfall([
- async.apply(runNpmCommand, 'install', id, version || 'latest'),
+ async.apply(runPackageManagerCommand, 'install', id, version || 'latest'),
function (next) {
Plugins.isActive(id, next);
},