Skip to content

Commit

Permalink
feat(type): add support for new "link:" dependency type
Browse files Browse the repository at this point in the history
  • Loading branch information
mgcrea committed Feb 28, 2017
1 parent d1a0ad1 commit 69f02c0
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 14 deletions.
4 changes: 2 additions & 2 deletions __tests__/package-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const path = require('path');
// regexp which verifies that cache path contains semver + hash
const cachePathRe = /-\d+\.\d+\.\d+-[\dabcdef]{40}$/;

function addTest(pattern, registry = 'npm', init: ?(cacheFolder: string) => Promise<any>, offline = false) {
function addTest(pattern, registry = 'npm', init: ?(cacheFolder: string) => Promise<any>, offline = false, linkFileDependencies = false) {
// concurrently network requests tend to stall
test(`${offline ? 'offline ' : ''}resolve ${pattern}`, async () => {
const lockfile = new Lockfile();
Expand All @@ -42,7 +42,7 @@ function addTest(pattern, registry = 'npm', init: ?(cacheFolder: string) => Prom
offline,
cacheFolder,
}, reporter);
const resolver = new PackageResolver(config, lockfile);
const resolver = new PackageResolver(config, lockfile, linkFileDependencies);
await resolver.init([{pattern, registry}]);

const ref = resolver.getPackageReferences()[0];
Expand Down
6 changes: 3 additions & 3 deletions src/cli/commands/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ class ImportPackageRequest extends PackageRequest {
}

class ImportPackageResolver extends PackageResolver {
constructor(config: Config, lockfile: Lockfile) {
super(config, lockfile);
constructor(config: Config, lockfile: Lockfile, linkFileDependencies: bool) {
super(config, lockfile, linkFileDependencies);
this.next = [];
this.rootName = 'root';
}
Expand Down Expand Up @@ -270,7 +270,7 @@ export class Import extends Install {
lockfile: Lockfile,
) {
super(flags, config, reporter, lockfile);
this.resolver = new ImportPackageResolver(this.config, this.lockfile);
this.resolver = new ImportPackageResolver(this.config, this.lockfile, false);
this.fetcher = new PackageFetcher(config, this.resolver);
this.compatibility = new PackageCompatibility(config, this.resolver, this.flags.ignoreEngines);
this.linker = new PackageLinker(config, this.resolver);
Expand Down
4 changes: 3 additions & 1 deletion src/cli/commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Flags = {
ignoreScripts: boolean,
ignoreOptional: boolean,
linkDuplicates: boolean,
linkFileDependencies: boolean,
force: boolean,
flat: boolean,
lockfile: boolean,
Expand Down Expand Up @@ -130,6 +131,7 @@ function normalizeFlags(config: Config, rawFlags: Object): Flags {
skipIntegrity: !!rawFlags.skipIntegrity,
frozenLockfile: !!rawFlags.frozenLockfile,
linkDuplicates: !!rawFlags.linkDuplicates,
linkFileDependencies: !!rawFlags.linkFileDependencies,

// add
peer: !!rawFlags.peer,
Expand Down Expand Up @@ -177,7 +179,7 @@ export class Install {
this.config = config;
this.flags = normalizeFlags(config, flags);

this.resolver = new PackageResolver(config, lockfile);
this.resolver = new PackageResolver(config, lockfile, this.flags.linkFileDependencies);
this.fetcher = new PackageFetcher(config, this.resolver);
this.compatibility = new PackageCompatibility(config, this.resolver, this.flags.ignoreEngines);
this.linker = new PackageLinker(config, this.resolver);
Expand Down
1 change: 1 addition & 0 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ commander.option('--no-lockfile', "don't read or generate a lockfile");
commander.option('--pure-lockfile', "don't generate a lockfile");
commander.option('--frozen-lockfile', "don't generate a lockfile and fail if an update is needed");
commander.option('--link-duplicates', 'create hardlinks to the repeated modules in node_modules');
commander.option('--link-file-dependencies', 'create symlinks for "file:" dependencies');
commander.option('--global-folder <path>', '');
commander.option(
'--modules-folder <path>',
Expand Down
1 change: 1 addition & 0 deletions src/fetchers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export type FetcherNames =
| 'base'
| 'copy'
| 'git'
| 'link'
| 'tarball';
8 changes: 8 additions & 0 deletions src/package-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {Reporter} from './reporters/index.js';
import type PackageReference from './package-reference.js';
import type Config from './config.js';
import {MessageError} from './errors.js';
import type {Manifest} from './types.js';
import * as fetchers from './fetchers/index.js';
import * as fs from './util/fs.js';
import * as promise from './util/promise.js';
Expand Down Expand Up @@ -37,6 +38,13 @@ export default class PackageFetcher {
const dest = this.config.generateHardModulePath(ref);

const remote = ref.remote;

// Mock metedata for linked dependencies
if (remote.type === 'link') {
const mockPkg: Manifest = {_uid: '', name: '', version: '0.0.0'};
return Promise.resolve({resolved: null, hash: '', dest, package: mockPkg, cached: false});
}

const Fetcher = fetchers[remote.type];
if (!Fetcher) {
throw new MessageError(this.reporter.lang('unknownFetcherFor', remote.type));
Expand Down
15 changes: 10 additions & 5 deletions src/package-linker.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,20 @@ export default class PackageLinker {
const hardlinksEnabled = linkDuplicates && await fs.hardlinksWork(this.config.cwd);

const copiedSrcs: Map<string, string> = 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);

// get a list of build artifacts contained in this module so we can prevent them from being marked as
// extraneous
const metadata = await this.config.readPackageMetadata(src);
for (const file of metadata.artifacts) {
artifactFiles.push(path.join(dest, file));
// extraneous, does not apply to linked dependencies
if (remote.type !== 'link') {
const metadata = await this.config.readPackageMetadata(src);
for (const file of metadata.artifacts) {
artifactFiles.push(path.join(dest, file));
}
}

const copiedDest = copiedSrcs.get(src);
Expand All @@ -148,6 +152,7 @@ export default class PackageLinker {
copyQueue.set(dest, {
src,
dest,
type: remote.type,
onFresh() {
if (ref) {
ref.setFresh(true);
Expand Down
6 changes: 5 additions & 1 deletion src/package-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const invariant = require('invariant');
const semver = require('semver');

export default class PackageResolver {
constructor(config: Config, lockfile: Lockfile) {
constructor(config: Config, lockfile: Lockfile, linkFileDependencies: bool) {
this.patternsByPackage = map();
this.fetchingPatterns = map();
this.fetchingQueue = new BlockingQueue('resolver fetching');
Expand All @@ -25,6 +25,7 @@ export default class PackageResolver {
this.flat = false;

this.reporter = config.reporter;
this.linkFileDependencies = linkFileDependencies;
this.lockfile = lockfile;
this.config = config;
}
Expand Down Expand Up @@ -64,6 +65,9 @@ export default class PackageResolver {
[packageName: string]: Array<string>
};

// whether the file dependencies will be linked
linkFileDependencies: boolean;

// lockfile instance which we can use to retrieve version info
lockfile: Lockfile;

Expand Down
20 changes: 19 additions & 1 deletion src/resolvers/exotics/file-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type {Manifest} from '../../types.js';
import type PackageRequest from '../../package-request.js';
import type {RegistryNames} from '../../registries/index.js';
import {MessageError} from '../../errors.js';
import ExoticResolver from './exotic-resolver.js';
import * as util from '../../util/misc.js';
Expand Down Expand Up @@ -29,6 +30,23 @@ export default class FileResolver extends ExoticResolver {
if (!path.isAbsolute(loc)) {
loc = path.join(this.config.cwd, loc);
}

if (this.resolver.linkFileDependencies) {
const registry: RegistryNames = 'npm';
const manifest: Manifest = {_uid: '', name: '', version: '0.0.0', _registry: registry};

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

manifest._uid = manifest.version;

return manifest;
}

if (!(await fs.exists(loc))) {
throw new MessageError(this.reporter.lang('doesntExist', loc));
}
Expand All @@ -49,7 +67,7 @@ export default class FileResolver extends ExoticResolver {
// Normalize relative paths; if anything changes, make a copy of the manifest
const dependencies = this.normalizeDependencyPaths(manifest.dependencies, loc);
const optionalDependencies = this.normalizeDependencyPaths(manifest.optionalDependencies, loc);

if (dependencies !== manifest.dependencies || optionalDependencies !== manifest.optionalDependencies) {
const _manifest = Object.assign({}, manifest);
if (dependencies != null) {
Expand Down
41 changes: 41 additions & 0 deletions src/resolvers/exotics/link-resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* @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 LinkResolver 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: '0.0.0', _registry: registry};

manifest._remote = {
type: 'link',
registry,
hash: null,
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 @@ -14,6 +14,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 @@ -23,6 +24,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 @@ -42,6 +42,7 @@ const noop = () => {};
export type CopyQueueItem = {
src: string,
dest: string,
type?: string,
onFresh?: ?() => void,
onDone?: ?() => void,
};
Expand Down Expand Up @@ -129,11 +130,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 69f02c0

Please sign in to comment.