pnpm
Version:
Fast, disk space efficient package manager
123 lines • 5.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const logger_1 = require("@pnpm/logger");
const server_1 = require("@pnpm/server");
const store_path_1 = require("@pnpm/store-path");
const Diable = require("diable");
const getPort = require("get-port");
const isWindows = require("is-windows");
const mkdirp = require("mkdirp-promise");
const fs = require("mz/fs");
const path = require("path");
const onExit = require("signal-exit");
const createStore_1 = require("../../createStore");
const pnpmPkgJson_1 = require("../../pnpmPkgJson");
const serverConnectionInfoDir_1 = require("../../serverConnectionInfoDir");
exports.default = async (opts) => {
if (opts.protocol === 'ipc' && opts.port) {
throw new Error('Port cannot be selected when server communicates via IPC');
}
if (opts.background && !Diable.isDaemon()) {
Diable();
}
const pathOfStore = await store_path_1.default(opts.prefix, opts.store);
const connectionInfoDir = serverConnectionInfoDir_1.default(pathOfStore);
const serverJsonPath = path.join(connectionInfoDir, 'server.json');
await mkdirp(connectionInfoDir);
// Open server.json with exclusive write access to ensure only one process can successfully
// start the server. Note: NFS does not support exclusive writing, but do we really care?
// Source: https://github.com/moxystudio/node-proper-lockfile#user-content-comparison
let fd;
try {
fd = await fs.open(serverJsonPath, 'wx');
}
catch (error) {
if (error.code !== 'EEXIST') {
throw error;
}
const err = new Error(`Canceling startup of server (pid ${process.pid}) because another process got exclusive access to server.json`);
err['code'] = 'ERR_PNPM_SERVER_MANIFEST_LOCKED'; // tslint:disable-line:no-string-literal
throw err;
}
let server = null;
onExit(() => {
if (server !== null) {
// Note that server.close returns a Promise, but we cannot wait for it because we may be
// inside the 'exit' even of process.
server.close(); // tslint:disable-line:no-floating-promises
}
if (fd !== null) {
try {
fs.closeSync(fd);
}
catch (error) {
logger_1.storeLogger.error(error, `Got error while closing file descriptor of server.json, but the process is already exiting`);
}
}
try {
fs.unlinkSync(serverJsonPath);
}
catch (error) {
if (error.code !== 'ENOENT') {
logger_1.storeLogger.error(error, `Got error unlinking server.json, but the process is already exiting`);
}
}
});
const store = await createStore_1.default(Object.assign(opts, {
store: pathOfStore,
}));
const protocol = opts.protocol || opts.port && 'tcp' || 'auto';
const serverOptions = await getServerOptions(connectionInfoDir, { protocol, port: opts.port });
const connectionOptions = {
remotePrefix: serverOptions.path
? `http://unix:${serverOptions.path}:`
: `http://${serverOptions.hostname}:${serverOptions.port}`,
};
server = server_1.createServer(store.ctrl, Object.assign({}, serverOptions, { ignoreStopRequests: opts.ignoreStopRequests, ignoreUploadRequests: opts.ignoreUploadRequests }));
// Make sure to populate server.json after the server has started, so clients know that the server is
// listening if a server.json with valid JSON content exists.
const serverJson = {
connectionOptions,
pid: process.pid,
pnpmVersion: pnpmPkgJson_1.default.version,
};
const serverJsonStr = JSON.stringify(serverJson, undefined, 2); // undefined and 2 are for formatting.
const serverJsonBuffer = Buffer.from(serverJsonStr, 'utf8');
// fs.write on NodeJS 4 requires the parameters offset and length to be set:
// https://nodejs.org/docs/latest-v4.x/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback
await fs.write(fd, serverJsonBuffer, 0, serverJsonBuffer.byteLength);
const fdForClose = fd;
// Set fd to null so we only attempt to close it once.
fd = null;
await fs.close(fdForClose);
};
async function getServerOptions(connectionInfoDir, opts) {
switch (opts.protocol) {
case 'tcp':
return getTcpOptions();
case 'ipc':
if (isWindows()) {
throw new Error('IPC protocol is not supported on Windows currently');
}
return getIpcOptions();
case 'auto':
if (isWindows()) {
return getTcpOptions();
}
return getIpcOptions();
default:
throw new Error(`Protocol ${opts.protocol} is not supported`);
}
async function getTcpOptions() {
return {
hostname: 'localhost',
port: opts.port || await getPort({ port: 5813 }),
};
}
function getIpcOptions() {
return {
path: path.join(connectionInfoDir, 'socket'),
};
}
}
//# sourceMappingURL=start.js.map