Skip to content

Commit

Permalink
Overrides the npm_config_registry var when doing info calls
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebatchelor committed Sep 14, 2018
1 parent c73f715 commit 69c1c55
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/utils/npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,19 @@ export function info(pkgName: string) {
return npmRequestLimit(async () => {
logger.info(messages.npmInfo(pkgName));

// Due to a couple of issues with yarnpkg, we also want to override the npm registry when doing
// npm info.
// Issues: We sometimes get back cached responses, i.e old data about packages which causes
// `publish` to behave incorrectly. It can also cause issues when publishing private packages
// as they will always give a 404, which will tell `publish` to always try to publish.
// See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633
const envOverride = {
npm_config_registry: 'https://registry.npmjs.org/'

This comment has been minimized.

Copy link
@jamiebuilds

jamiebuilds Sep 14, 2018

Member

this registry name shouldn't be hardcoded

This comment has been minimized.

Copy link
@lukebatchelor

lukebatchelor Sep 14, 2018

Author Member

I know. We called it out in the issue for it.

And we're gonna see if there's a clean way to pull it from npm properly. Right now we just want to fix yarns hack (it's not going to break any new people, who weren't broken before because of yarn being bad)

This comment has been minimized.

Copy link
@lukebatchelor

lukebatchelor Sep 17, 2018

Author Member

Created #193

};

let result = await processes.spawn('npm', ['info', pkgName, '--json'], {
silent: true
silent: true,
env: Object.assign({}, process.env, envOverride)
});

return JSON.parse(result.stdout);
Expand Down
219 changes: 219 additions & 0 deletions src/utils/symlinkPackageBinsToProject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// @flow

This comment has been minimized.

Copy link
@lukebatchelor

lukebatchelor Sep 14, 2018

Author Member

This is definitely an embarrassing error.

I did git add src/utils thinking that would avoid the other work I had open... My bad...

This comment has been minimized.

Copy link
@lukebatchelor

lukebatchelor Sep 14, 2018

Author Member

I've reverted it

import path from 'path';
import pathIsInside from 'path-is-inside';
import includes from 'array-includes';

import Project from '../Project';
import Package from '../Package';
import { BoltError } from './errors';
import * as fs from './fs';
import * as logger from './logger';
import * as messages from './messages';
import * as yarn from './yarn';

export default async function symlinkPackageDependencies(
project: Project,
pkg: Package,
dependencies: Array<string>
) {
let projectDeps = project.pkg.getAllDependencies();
let pkgDependencies = project.pkg.getAllDependencies();
let packages = await project.getPackages();
let {
graph: dependencyGraph,
valid: dependencyGraphValid
} = await project.getDependencyGraph(packages);
let pkgName = pkg.config.getName();
// get all the dependencies that are internal workspaces in this project
let internalDeps = (dependencyGraph.get(pkgName) || {}).dependencies || [];

let directoriesToCreate = [];
let symlinksToCreate = [];

let valid = true;

/*********************************************************************
* Calculate all the external dependencies that need to be symlinked *
**********************************************************************/

directoriesToCreate.push(pkg.nodeModules, pkg.nodeModulesBin);

for (let depName of dependencies) {
let versionInProject = project.pkg.getDependencyVersionRange(depName);
let versionInPkg = pkg.getDependencyVersionRange(depName);

// If dependency is internal we can ignore it (we symlink below)
if (dependencyGraph.has(depName)) {
continue;
}

if (!versionInProject) {
valid = false;
logger.error(
messages.depMustBeAddedToProject(pkg.config.getName(), depName)
);
continue;
}

if (!versionInPkg) {
valid = false;
logger.error(
messages.couldntSymlinkDependencyNotExists(
pkg.config.getName(),
depName
)
);
continue;
}

if (versionInProject !== versionInPkg) {
valid = false;
logger.error(
messages.depMustMatchProject(
pkg.config.getName(),
depName,
versionInProject,
versionInPkg
)
);
continue;
}

let src = path.join(project.pkg.nodeModules, depName);
let dest = path.join(pkg.nodeModules, depName);

symlinksToCreate.push({ src, dest, type: 'junction' });
}

/*********************************************************************
* Calculate all the internal dependencies that need to be symlinked *
**********************************************************************/

for (let dependency of internalDeps) {
let depWorkspace = dependencyGraph.get(dependency) || {};
let src = depWorkspace.pkg.dir;
let dest = path.join(pkg.nodeModules, dependency);

symlinksToCreate.push({ src, dest, type: 'junction' });
}

if (!dependencyGraphValid || !valid) {
throw new BoltError('Cannot symlink invalid set of dependencies.');
}

/********************************************************
* Calculate all the bin files that need to be symlinked *
*********************************************************/
let projectBinFiles = await fs.readdirSafe(project.pkg.nodeModulesBin);

// TODO: For now, we'll search through each of the bin files in the Project and find which ones are
// dependencies we are symlinking. In the future, we should really be going through each dependency
// and all of its dependencies and checking which ones expose bins so that all the transitive ones
// are included too

for (let binFile of projectBinFiles) {
let binPath = path.join(project.pkg.nodeModulesBin, binFile);
let binName = path.basename(binPath);

// read the symlink to find the actual bin file (path will be relative to the symlink)
let actualBinFileRelative = await fs.readlink(binPath);

if (!actualBinFileRelative) {
throw new BoltError(`${binName} is not a symlink`);
}

let actualBinFile = path.join(
project.pkg.nodeModulesBin,
actualBinFileRelative
);

if (!pathIsInside(actualBinFile, project.pkg.nodeModules)) {
throw new BoltError(
`${binName} is linked to a location outside of project node_modules: ${actualBinFileRelative}`
);
}

// To find the name of the dep that created the bin we'll get its path from node_modules and
// use the first one or two parts (two if the package is scoped)
let binFileRelativeToNodeModules = path.relative(
project.pkg.nodeModules,
actualBinFile
);
let pathParts = binFileRelativeToNodeModules.split(path.sep);
let pkgName = pathParts[0];

if (pkgName.startsWith('@')) {
pkgName += '/' + pathParts[1];
}

let workspaceBinPath = path.join(pkg.nodeModulesBin, binName);

symlinksToCreate.push({
src: binPath,
dest: workspaceBinPath,
type: 'exec'
});
}

/*****************************************************************
* Calculate all the internal bin files that need to be symlinked *
******************************************************************/

// TODO: Same as above, we should really be making sure we get all the transitive bins as well

for (let dependency of internalDeps) {
let depWorkspace = dependencyGraph.get(dependency) || {};
let depBinFiles =
depWorkspace.pkg &&
depWorkspace.pkg.config &&
depWorkspace.pkg.config.getBin();

if (!depBinFiles) {
continue;
}

if (!includes(dependencies, dependency)) {
// dependency is not one we are supposed to symlink right now
continue;
}

if (typeof depBinFiles === 'string') {
// package may be scoped, name will only be the second part
let binName = dependency.split('/').pop();
let src = path.join(depWorkspace.pkg.dir, depBinFiles);
let dest = path.join(pkg.nodeModulesBin, binName);

symlinksToCreate.push({ src, dest, type: 'exec' });
continue;
}

for (let [binName, binPath] of Object.entries(depBinFiles)) {
let src = path.join(depWorkspace.pkg.dir, String(binPath));
let dest = path.join(pkg.nodeModulesBin, binName);

symlinksToCreate.push({ src, dest, type: 'exec' });
}
}

/**********************************
* Create directories and symlinks *
***********************************/

await yarn.runIfExists(pkg, 'preinstall');

await Promise.all(
directoriesToCreate.map(dirName => {
return fs.mkdirp(dirName);
})
);

await Promise.all(
symlinksToCreate.map(async ({ src, dest, type }) => {
await fs.symlink(src, dest, type);
})
);

await yarn.runIfExists(pkg, 'postinstall');
await yarn.runIfExists(pkg, 'prepublish');
await yarn.runIfExists(pkg, 'prepare');
}

0 comments on commit 69c1c55

Please sign in to comment.