@vltpkg/vlx 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,6 +41,7 @@ const arg0 = await vlx.resolve(positionals, options, promptFn)
41
41
  // now one of the following is true:
42
42
  // - arg0 refers to a locally installed package bin
43
43
  // - arg0 refers to a package bin in the XDG.data directory
44
+ // - arg0 refers to a local directory's bin, shimmed in XDG.cache
44
45
  // - arg0 is undefined, and an interactive shell can be executed
45
46
  // - an error was thrown, because it couijld not be determined
46
47
  if (arg0 === undefined) {
package/dist/index.d.ts CHANGED
@@ -22,8 +22,9 @@ export type VlxInfo = VlxManifest['vlx'] & {
22
22
  /** version of the package being run */
23
23
  version?: string;
24
24
  /**
25
- * path to the synthetic project for non-local installs, or to the
26
- * current projectRoot if the resolution is local.
25
+ * path to the synthetic project for non-local installs, to the
26
+ * current projectRoot if the resolution is local, or a private shim
27
+ * dir for local directory specs.
27
28
  */
28
29
  path: string;
29
30
  /**
@@ -0,0 +1,15 @@
1
+ import type { PackageJson } from '@vltpkg/package-json';
2
+ import type { Spec } from '@vltpkg/spec';
3
+ import type { VlxInfo } from './index.ts';
4
+ /**
5
+ * Absolute path if the spec is a local directory (relative to cwd)
6
+ */
7
+ export declare const localDir: (spec: Spec) => Promise<string | undefined>;
8
+ /**
9
+ * Shim a local directory's bins into a private bin dir, so they run
10
+ * in place. Nothing is installed.
11
+ *
12
+ * The dir is keyed by all the shims depend on (dir, bins, shebangs),
13
+ * and never changed once made, so concurrent runs are safe.
14
+ */
15
+ export declare const vlxLocal: (dir: string, packageJson: PackageJson) => Promise<VlxInfo>;
package/dist/local.js ADDED
@@ -0,0 +1,69 @@
1
+ import { cmdShim } from '@vltpkg/cmd-shim';
2
+ import { error } from '@vltpkg/error-cause';
3
+ import { RollbackRemove } from '@vltpkg/rollback-remove';
4
+ import { XDG } from '@vltpkg/xdg';
5
+ import { createHash } from 'node:crypto';
6
+ import { mkdir, mkdtemp, readFile, realpath, rename, rm, } from 'node:fs/promises';
7
+ import { basename, resolve } from 'node:path';
8
+ import { pathToFileURL } from 'node:url';
9
+ import { dirExists } from "./dir-exists.js";
10
+ import { inferDefaultExecutable } from "./infer-default-executable.js";
11
+ /**
12
+ * Absolute path if the spec is a local directory (relative to cwd)
13
+ */
14
+ export const localDir = async (spec) => {
15
+ const { type, file } = spec.final;
16
+ if (type !== 'file' || !file)
17
+ return;
18
+ const dir = resolve(file);
19
+ return (await dirExists(dir)) ? dir : undefined;
20
+ };
21
+ /**
22
+ * Shim a local directory's bins into a private bin dir, so they run
23
+ * in place. Nothing is installed.
24
+ *
25
+ * The dir is keyed by all the shims depend on (dir, bins, shebangs),
26
+ * and never changed once made, so concurrent runs are safe.
27
+ */
28
+ export const vlxLocal = async (dir, packageJson) => {
29
+ const manifest = packageJson.read(dir);
30
+ const bins = Object.entries(manifest.bin ?? {});
31
+ const heads = await Promise.all(bins.map(async ([k, v]) => {
32
+ const target = resolve(dir, v);
33
+ const data = await readFile(target, 'utf8').catch((cause) => {
34
+ throw error(`Bin target not found: ${k} -> ${v}`, {
35
+ path: target,
36
+ cause,
37
+ });
38
+ });
39
+ return `\0${k}\0${v}\0${data.trim().split(/\r*\n/)[0]}`;
40
+ }));
41
+ const key = createHash('sha512')
42
+ .update(dir + heads.join(''))
43
+ .digest('hex')
44
+ .substring(0, 16);
45
+ const cache = new XDG('vlt/vlx-local').cache();
46
+ await mkdir(cache, { recursive: true });
47
+ // real path, so the shims' relative `..` resolve as expected
48
+ const path = resolve(await realpath(cache), key);
49
+ if (!(await dirExists(path))) {
50
+ // build aside, then move into place
51
+ const tmp = await mkdtemp(`${path}-`);
52
+ const remover = new RollbackRemove();
53
+ await Promise.all(bins.map(([k, v]) => cmdShim(resolve(dir, v), resolve(tmp, 'node_modules/.bin', k), remover)));
54
+ remover.confirm();
55
+ await rename(tmp, path).catch(async (er) => {
56
+ // another run made it first
57
+ if (!(await dirExists(path)))
58
+ throw er;
59
+ await rm(tmp, { recursive: true, force: true });
60
+ });
61
+ }
62
+ return {
63
+ path,
64
+ name: manifest.name ?? basename(dir),
65
+ version: manifest.version,
66
+ resolved: String(pathToFileURL(dir)),
67
+ arg0: inferDefaultExecutable(manifest)?.[0],
68
+ };
69
+ };
package/dist/resolve.js CHANGED
@@ -5,6 +5,7 @@ import { addToPATH } from "./add-to-path.js";
5
5
  import { findExecutable } from "./find-executable.js";
6
6
  import { findPackage } from "./find-package.js";
7
7
  import { vlxInstall } from "./install.js";
8
+ import { localDir, vlxLocal } from "./local.js";
8
9
  /**
9
10
  * Figure out the `arg0` to use for an exec command.
10
11
  *
@@ -22,7 +23,10 @@ export const vlxResolve = async (positionals, options, promptFn) => {
22
23
  // caller will run interactive shell
23
24
  return undefined;
24
25
  }
25
- const found = await findExecutable(arg0, projectRoot);
26
+ // path-like args are never bin names
27
+ const found = arg0 === '.' || arg0 === '..' || /[\\/]/.test(arg0) ?
28
+ undefined
29
+ : await findExecutable(arg0, projectRoot);
26
30
  // if found locally, then that's the bin, whatever the package is
27
31
  // since no package option was provided anyway.
28
32
  if (found)
@@ -33,18 +37,28 @@ export const vlxResolve = async (positionals, options, promptFn) => {
33
37
  arg0 = undefined;
34
38
  }
35
39
  let pkgTarget = undefined;
40
+ let dir;
36
41
  if (pkgOption) {
37
- // check for local option, otherwise install in data dir we can
38
- // ONLY do the local option if the spec is a simple name. otherwise
39
- // it's too complicated to know if it's a match, especially in this
40
- // world of multiple registries and such, and it's easy enough to
41
- // just install it externally.
42
- const pkgSpec = Spec.parseArgs(pkgOption, options);
42
+ // check for local option, otherwise install in data dir. local is
43
+ // a simple name found in node_modules, or a local dir run in place.
44
+ // other specs are too complicated to know if it's a match, especially
45
+ // in this world of multiple registries and such, and it's easy enough
46
+ // to just install them externally.
47
+ const pkgSpec = Spec.parseArgs(
48
+ // bare . / .. are paths, not names
49
+ pkgOption === '.' || pkgOption === '..' ?
50
+ `${pkgOption}/`
51
+ : pkgOption, options);
43
52
  const { name, bareSpec } = pkgSpec.final;
44
53
  // if it's just a name, we can use the local version
45
54
  if (!bareSpec) {
46
55
  pkgTarget = await findPackage(name, projectRoot, packageJson);
47
56
  }
57
+ else {
58
+ dir = await localDir(pkgSpec);
59
+ if (dir)
60
+ pkgTarget = await vlxLocal(dir, packageJson);
61
+ }
48
62
  pkgTarget ??= await vlxInstall(pkgSpec, options, promptFn);
49
63
  }
50
64
  if (pkgTarget) {
@@ -71,7 +85,8 @@ export const vlxResolve = async (positionals, options, promptFn) => {
71
85
  // infer arg0 from the manifst found at pkgTarget[1]
72
86
  arg0 = pkgTarget.arg0;
73
87
  if (!arg0) {
74
- const { name, version, bin } = packageJson.read(resolve(pkgTarget.path, 'node_modules', pkgTarget.name));
88
+ const { name, version, bin } = packageJson.read(dir ??
89
+ resolve(pkgTarget.path, 'node_modules', pkgTarget.name));
75
90
  throw error('Package executable could not be inferred', {
76
91
  name,
77
92
  version,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/vlx",
3
3
  "description": "vlt exec management",
4
- "version": "1.3.6",
4
+ "version": "1.3.8",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,15 +13,15 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/cmd-shim": "1.3.6",
17
- "@vltpkg/error-cause": "1.3.6",
18
- "@vltpkg/graph": "1.3.6",
19
- "@vltpkg/package-info": "1.3.6",
20
- "@vltpkg/package-json": "1.3.6",
21
- "@vltpkg/rollback-remove": "1.3.6",
22
- "@vltpkg/spec": "1.3.6",
23
- "@vltpkg/types": "1.3.6",
24
- "@vltpkg/xdg": "1.3.6",
16
+ "@vltpkg/cmd-shim": "1.3.8",
17
+ "@vltpkg/error-cause": "1.3.8",
18
+ "@vltpkg/graph": "1.3.8",
19
+ "@vltpkg/package-info": "1.3.8",
20
+ "@vltpkg/package-json": "1.3.8",
21
+ "@vltpkg/rollback-remove": "1.3.8",
22
+ "@vltpkg/spec": "1.3.8",
23
+ "@vltpkg/types": "1.3.8",
24
+ "@vltpkg/xdg": "1.3.8",
25
25
  "path-scurry": "^2.0.1",
26
26
  "walk-up-path": "^4.0.0"
27
27
  },