Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/FileSystemUtilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ export default class FileSystemUtilities {
fs.writeFile(filePath, ensureEndsWithNewLine(fileContents), callback);
}

@logger.logifyAsync()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like most FileSystemUtilities have tests; seems worthwhile to add some for these as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good call. Added some more tests.

static rename(from, to, callback) {
fs.rename(from, to, callback);
}

@logger.logifySync()
static renameSync(from, to) {
fs.renameSync(from, to);
}

@logger.logifySync()
static writeFileSync(filePath, fileContents) {
fs.writeFileSync(filePath, ensureEndsWithNewLine(fileContents));
Expand Down
58 changes: 52 additions & 6 deletions src/NpmUtilities.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import ChildProcessUtilities from "./ChildProcessUtilities";
import FileSystemUtilities from "./FileSystemUtilities";
import onExit from "signal-exit";
import logger from "./logger";
import escapeArgs from "command-join";
import path from "path";
Expand All @@ -11,18 +13,62 @@ export default class NpmUtilities {
// Nothing to do if we weren't given any deps.
if (!(dependencies && dependencies.length)) return callback();

let args = ["install"];

if (dependencies) {
args = args.concat(dependencies);
}
const args = ["install"];

const opts = {
cwd: directory,
stdio: ["ignore", "pipe", "pipe"],
};

ChildProcessUtilities.spawn("npm", args, opts, callback);
const packageJson = path.join(directory, "package.json");
const packageJsonBkp = packageJson + ".lerna_backup";

FileSystemUtilities.rename(packageJson, packageJsonBkp, (err) => {
if (err) return callback(err);

const cleanup = () => {

// Need to do this one synchronously because we might be doing it on exit.
FileSystemUtilities.renameSync(packageJsonBkp, packageJson);
};

// If we die we need to be sure to put things back the way we found them.
const unregister = onExit(cleanup);

// Construct a basic fake package.json with just the deps we need to install.
const tempJson = JSON.stringify({
dependencies: dependencies.reduce((deps, dep) => {
const [pkg, version] = NpmUtilities.splitVersion(dep);
deps[pkg] = version || "*";
return deps;
}, {})
});

// Write out our temporary cooked up package.json and then install.
FileSystemUtilities.writeFile(packageJson, tempJson, (err) => {

// We have a few housekeeping tasks to take care of whether we succeed or fail.
const done = (err) => {
cleanup();
unregister();
callback(err);
};

if (err) {
return done(err);
} else {
ChildProcessUtilities.spawn("npm", args, opts, done);
}
});
});
}

// Take a dep like "foo@^1.0.0".
// Return a tuple like ["foo", "^1.0.0"].
// Handles scoped packages.
// Returns undefined for version if none specified.
static splitVersion(dep) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I share @rygine's enthusiasm for regexes, but it seems that you could write tests for splitVersion directly that verified that it works as intended, which would make me feel much better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added some tests.

return dep.match(/^(@?[^@]+)(?:@(.+))?/).slice(1, 3);
}

@logger.logifySync()
Expand Down
Loading