Skip to content

Commit

Permalink
add symlink link: dependency type (fixes #884)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgcrea committed Oct 16, 2016
1 parent 8f4f01e commit 1631460
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 5 deletions.
17 changes: 17 additions & 0 deletions __tests__/fetchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import TarballFetcher, {LocalTarballFetcher} from '../src/fetchers/tarball-fetch
import BaseFetcher from '../src/fetchers/base-fetcher.js';
import CopyFetcher from '../src/fetchers/copy-fetcher.js';
import GitFetcher from '../src/fetchers/git-fetcher.js';
import LinkFetcher from '../src/fetchers/link-fetcher.js';
import {NoopReporter} from '../src/reporters/index.js';
import Config from '../src/config.js';
import mkdir from './_temp.js';
Expand Down Expand Up @@ -55,6 +56,22 @@ test('CopyFetcher.fetch', async () => {
expect(contentFoo).toBe('bar');
});

test('LinkFetcher.fetch', async () => {
const a = await mkdir('copy-fetcher-a');
await fs.writeFile(path.join(a, 'package.json'), '{}');
await fs.writeFile(path.join(a, 'foo'), 'bar');

const b = await mkdir('copy-fetcher-b');
const fetcher = new LinkFetcher(b, {
type: 'link',
reference: a,
registry: 'npm',
}, await createConfig());
await fetcher.fetch();
const stat = await fs.lstat(b);
expect(stat.isDirectory()).toEqual(true);
});

test('GitFetcher.fetch', async () => {
const dir = await mkdir('git-fetcher');
const fetcher = new GitFetcher(dir, {
Expand Down
3 changes: 3 additions & 0 deletions __tests__/package-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ addTest('react-native'); // npm
addTest('ember-cli'); // npm
addTest('npm:gulp'); // npm
addTest('@polymer/iron-icon'); // npm scoped package
addTest(`file:${__dirname}/../node_modules/babel-core`); // file
addTest(`link:${__dirname}/../node_modules/eslint`); // existing link
addTest(`link:./../not-created-yet`); // not existing link
8 changes: 7 additions & 1 deletion src/fetchers/base-fetcher.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @flow */
/* eslint no-unused-vars: 0 */

import type {PackageRemote, FetchedMetadata, FetchedOverride} from '../types.js';
import type {PackageRemote, FetchedMetadata, FetchedOverride, Manifest} from '../types.js';
import type {RegistryNames} from '../registries/index.js';
import type Config from '../config.js';
import * as constants from '../constants.js';
Expand Down Expand Up @@ -45,6 +45,12 @@ export default class BaseFetcher {
// fetch package and get the hash
const {hash, resolved} = await this._fetch();

// skip any readManifest operation for link type as dest might not exist yet
if (this.remote.type === 'link') {
const mockPkg: Manifest = {_uid: '', name: '', version: 'link', _registry: this.registry};
return Promise.resolve({resolved, hash, dest, package: mockPkg});
}

// load the new normalized manifest
const pkg = await this.config.readManifest(dest, this.registry);

Expand Down
4 changes: 4 additions & 0 deletions src/fetchers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
import BaseFetcher from './base-fetcher.js';
import CopyFetcher from './copy-fetcher.js';
import GitFetcher from './git-fetcher.js';
import LinkFetcher from './link-fetcher.js';
import TarballFetcher from './tarball-fetcher.js';

export {BaseFetcher as base};
export {CopyFetcher as copy};
export {GitFetcher as git};
export {LinkFetcher as link};
export {TarballFetcher as tarball};

export type Fetchers =
| BaseFetcher
| CopyFetcher
| GitFetcher
| LinkFetcher
| TarballFetcher;

export type FetcherNames =
| 'base'
| 'copy'
| 'git'
| 'link'
| 'tarball';
14 changes: 14 additions & 0 deletions src/fetchers/link-fetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* @flow */

import type {FetchedOverride} from '../types.js';
import BaseFetcher from './base-fetcher.js';

export default class LinkFetcher extends BaseFetcher {
_fetch(): Promise<FetchedOverride> {
// nothing to fetch
return Promise.resolve({
hash: this.hash || '',
resolved: null,
});
}
}
17 changes: 14 additions & 3 deletions src/package-linker.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,25 @@ export default class PackageLinker {

async copyModules(patterns: Array<string>): Promise<void> {
let flatTree = await this.getFlatHoistedTree(patterns);

// sorted tree makes file creation and copying not to interfere with each other
flatTree = flatTree.sort(function(dep1, dep2): number {
return dep1[0].localeCompare(dep2[0]);
});

//
const queue: Map<string, CopyQueueItem> = new Map();
for (const [dest, {pkg, loc: src}] of flatTree) {
for (const [dest, {pkg, loc}] of flatTree) {
const remote = pkg._remote || {type: ''};
const ref = pkg._reference;
const src = remote.type === 'link' ? remote.reference : loc;
invariant(ref, 'expected package reference');
ref.setLocation(dest);

queue.set(dest, {
src,
dest,
type: remote.type,
onFresh() {
if (ref) {
ref.setFresh(true);
Expand All @@ -148,7 +151,15 @@ export default class PackageLinker {
if (await fs.exists(loc)) {
const files = await fs.readdir(loc);
for (const file of files) {
possibleExtraneous.add(path.join(loc, file));
// scoped packages
if (file.startsWith('@')) {
const scopedFiles = await fs.readdir(path.join(loc, file));
for (const scopedFile of scopedFiles) {
possibleExtraneous.add(path.join(loc, file, scopedFile));
}
} else {
possibleExtraneous.add(path.join(loc, file));
}
}
}
}
Expand Down
40 changes: 40 additions & 0 deletions src/resolvers/exotics/link-resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* @flow */

import type {Manifest} from '../../types.js';
import type {RegistryNames} from '../../registries/index.js';
import type PackageRequest from '../../package-request.js';
import ExoticResolver from './exotic-resolver.js';
import * as util from '../../util/misc.js';

const path = require('path');

export default class FileResolver extends ExoticResolver {
constructor(request: PackageRequest, fragment: string) {
super(request, fragment);
this.loc = util.removePrefix(fragment, 'link:');
}

loc: string;

static protocol = 'link';

resolve(): Promise<Manifest> {
let loc = this.loc;
if (!path.isAbsolute(loc)) {
loc = path.join(this.config.cwd, loc);
}

const registry: RegistryNames = 'npm';
const manifest: Manifest = {_uid: '', name: '', version: 'link', _registry: registry};

manifest._remote = {
type: 'link',
registry,
reference: loc,
};

manifest._uid = manifest.version;

return Promise.resolve(manifest);
}
}
2 changes: 2 additions & 0 deletions src/resolvers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ExoticGit from './exotics/git-resolver.js';
import ExoticTarball from './exotics/tarball-resolver.js';
import ExoticGitHub from './exotics/github-resolver.js';
import ExoticFile from './exotics/file-resolver.js';
import ExoticLink from './exotics/link-resolver.js';
import ExoticGitLab from './exotics/gitlab-resolver.js';
import ExoticGist from './exotics/gist-resolver.js';
import ExoticBitbucket from './exotics/bitbucket-resolver.js';
Expand All @@ -25,6 +26,7 @@ export const exotics = {
tarball: ExoticTarball,
github: ExoticGitHub,
file: ExoticFile,
link: ExoticLink,
gitlab: ExoticGitLab,
gist: ExoticGist,
bitbucket: ExoticBitbucket,
Expand Down
15 changes: 14 additions & 1 deletion src/util/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const noop = () => {};
export type CopyQueueItem = {
src: string,
dest: string,
type?: string,
onFresh?: ?() => void,
onDone?: ?() => void,
};
Expand Down Expand Up @@ -113,11 +114,23 @@ async function buildActionsForCopy(

//
async function build(data): Promise<void> {
const {src, dest} = data;
const {src, dest, type} = data;
const onFresh = data.onFresh || noop;
const onDone = data.onDone || noop;
files.add(dest);

if (type === 'link') {
await mkdirp(path.dirname(dest));
onFresh();
actions.push({
type: 'symlink',
dest,
linkname: src,
});
onDone();
return;
}

if (events.ignoreBasenames.indexOf(path.basename(src)) >= 0) {
// ignored file
return;
Expand Down

0 comments on commit 1631460

Please sign in to comment.