Merge pull request #2035 from thelounge/astorije/ensure-packages-are-dirs

Ensure packages loaded are directories
This commit is contained in:
Pavel Djundik 2018-02-13 11:32:02 +02:00 committed by GitHub
commit e4701be708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 136 additions and 43 deletions

View File

@ -35,11 +35,10 @@ program
log.info(`Installing ${colors.green(packageName)}...`); log.info(`Installing ${colors.green(packageName)}...`);
const packagesPath = Helper.getPackagesPath(); const packagesPath = Helper.getPackagesPath();
const packagesParent = path.dirname(packagesPath); const packagesConfig = path.join(packagesPath, "package.json");
const packagesConfig = path.join(packagesParent, "package.json");
// Create node_modules folder, otherwise npm will start walking upwards to find one // Create node_modules folder, otherwise npm will start walking upwards to find one
fsextra.ensureDirSync(packagesPath); fsextra.ensureDirSync(path.join(packagesPath, "node_modules"));
// Create package.json with private set to true to avoid npm warnings, if // Create package.json with private set to true to avoid npm warnings, if
// it doesn't exist already // it doesn't exist already
@ -61,7 +60,7 @@ program
"--no-package-lock", "--no-package-lock",
"--no-progress", "--no-progress",
"--prefix", "--prefix",
packagesParent, packagesPath,
packageName, packageName,
], ],
{ {

View File

@ -22,8 +22,7 @@ program
log.info(`Uninstalling ${colors.green(packageName)}...`); log.info(`Uninstalling ${colors.green(packageName)}...`);
const packagesPath = Helper.getPackagesPath(); const packagesPath = Helper.getPackagesPath();
const packagesParent = path.dirname(packagesPath); const packagesConfig = path.join(packagesPath, "package.json");
const packagesConfig = path.join(packagesParent, "package.json");
const packageWasNotInstalled = `${colors.green(packageName)} was not installed.`; const packageWasNotInstalled = `${colors.green(packageName)} was not installed.`;
if (!fs.existsSync(packagesConfig)) { if (!fs.existsSync(packagesConfig)) {
@ -48,7 +47,7 @@ program
"--depth", "--depth",
"0", "0",
"--prefix", "--prefix",
packagesParent, packagesPath,
packageName, packageName,
], ],
{ {
@ -78,9 +77,10 @@ program
npm, npm,
[ [
"uninstall", "uninstall",
"--save",
"--no-progress", "--no-progress",
"--prefix", "--prefix",
packagesParent, packagesPath,
packageName, packageName,
], ],
{ {

View File

@ -75,7 +75,7 @@ function setHome(newPath) {
configPath = path.join(homePath, "config.js"); configPath = path.join(homePath, "config.js");
usersPath = path.join(homePath, "users"); usersPath = path.join(homePath, "users");
storagePath = path.join(homePath, "storage"); storagePath = path.join(homePath, "storage");
packagesPath = path.join(homePath, "packages", "node_modules"); packagesPath = path.join(homePath, "packages");
// Reload config from new home location // Reload config from new home location
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
@ -144,7 +144,7 @@ function getPackagesPath() {
} }
function getPackageModulePath(packageName) { function getPackageModulePath(packageName) {
return path.join(Helper.getPackagesPath(), packageName); return path.join(Helper.getPackagesPath(), "node_modules", packageName);
} }
function ip2hex(address) { function ip2hex(address) {

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const fs = require("fs");
const colors = require("colors/safe"); const colors = require("colors/safe");
const path = require("path");
const Helper = require("../../helper"); const Helper = require("../../helper");
const themes = require("./themes"); const themes = require("./themes");
const packageMap = new Map(); const packageMap = new Map();
@ -35,38 +35,43 @@ function getPackage(name) {
} }
function loadPackages() { function loadPackages() {
fs.readdir(Helper.getPackagesPath(), (err, packages) => { const packageJson = path.join(Helper.getPackagesPath(), "package.json");
if (err) { let packages;
return;
try {
packages = Object.keys(require(packageJson).dependencies);
} catch (e) {
packages = [];
} }
packages.forEach((packageName) => { packages.forEach((packageName) => {
const packageFile = getModuleInfo(packageName); const errorMsg = `Package ${colors.bold(packageName)} could not be loaded`;
if (!packageFile) { let packageInfo;
let packageFile;
try {
packageInfo = require(path.join(Helper.getPackageModulePath(packageName), "package.json"));
packageFile = require(Helper.getPackageModulePath(packageName));
} catch (e) {
log.warn(errorMsg);
return; return;
} }
if (!packageInfo.thelounge) {
log.warn(errorMsg);
return;
}
packageMap.set(packageName, packageFile); packageMap.set(packageName, packageFile);
if (packageFile.type === "theme") {
themes.addTheme(packageName, packageFile); if (packageInfo.type === "theme") {
themes.addTheme(packageName, packageInfo);
} }
if (packageFile.onServerStart) { if (packageFile.onServerStart) {
packageFile.onServerStart(packageApis(packageName)); packageFile.onServerStart(packageApis(packageName));
} }
});
});
}
function getModuleInfo(packageName) { log.info(`Package ${colors.bold(packageName)} loaded`);
let module; });
try {
module = require(Helper.getPackageModulePath(packageName));
} catch (e) {
log.warn(`Specified package ${colors.yellow(packageName)} is not installed in packages directory`);
return;
}
if (!module.thelounge) {
log.warn(`Specified package ${colors.yellow(packageName)} doesn't have required information.`);
return;
}
return module.thelounge;
} }

View File

@ -1,2 +1,5 @@
# files that may be generated by tests # Files that may be generated by tests
.lounge/storage/ .lounge/storage/
# Fixtures contain fake packages, stored in a fake node_modules folder
!.lounge/packages/node_modules/

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
onServerStart(apis) {
apis.Stylesheets.addFile("style.css");
},
};

View File

@ -0,0 +1,12 @@
{
"name": "thelounge-package-foo",
"private": true,
"main": "index.js",
"thelounge": {
"type": "package"
},
"keywords": [
"thelounge",
"thelounge-package"
]
}

View File

@ -0,0 +1,6 @@
{
"private": true,
"dependencies": {
"thelounge-package-foo": "*"
}
}

View File

@ -0,0 +1,61 @@
"use strict";
const expect = require("chai").expect;
const TestUtil = require("../../util");
let packages;
describe("packages", function() {
let originalLogInfo;
beforeEach(function() {
originalLogInfo = log.info;
log.info = () => {};
delete require.cache[require.resolve("../../../src/plugins/packages")];
packages = require("../../../src/plugins/packages");
});
afterEach(function() {
log.info = originalLogInfo;
});
describe(".getStylesheets", function() {
it("should contain no stylesheets before packages are loaded", function() {
expect(packages.getStylesheets()).to.be.empty;
});
it("should return the list of registered stylesheets for loaded packages", function() {
packages.loadPackages();
expect(packages.getStylesheets()).to.deep.equal([
"thelounge-package-foo/style.css",
]);
});
});
describe(".getPackage", function() {
it("should contain no reference to packages before loading them", function() {
expect(packages.getPackage("thelounge-package-foo")).to.be.undefined;
});
it("should return details of a registered package after it was loaded", function() {
packages.loadPackages();
expect(packages.getPackage("thelounge-package-foo"))
.to.have.key("onServerStart");
});
});
describe(".loadPackages", function() {
it("should display report about loading packages", function() {
// Mock `log.info` to extract its effect into a string
let stdout = "";
log.info = TestUtil.mockLogger((str) => stdout += str);
packages.loadPackages();
expect(stdout).to.deep.equal("Package thelounge-package-foo loaded\n");
});
});
});

View File

@ -27,7 +27,7 @@ function mockLogger(callback) {
return function() { return function() {
// TODO: Use ...args with The Lounge v3: add `...args` as function argument // TODO: Use ...args with The Lounge v3: add `...args` as function argument
// and replaced the next line with `args.join(", ")` // and replaced the next line with `args.join(", ")`
const stdout = Array.prototype.slice.call(arguments).join(", ") const stdout = Array.prototype.slice.call(arguments).join(" ")
.replace( // Removes ANSI colors. See https://stackoverflow.com/a/29497680 .replace( // Removes ANSI colors. See https://stackoverflow.com/a/29497680
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
"" ""