@metreeca/rack 0.5.0 → 0.6.1
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 +3 -0
- package/dist/node/index.d.ts +3 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +38 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/symlinks.d.ts +54 -0
- package/dist/node/symlinks.d.ts.map +1 -0
- package/dist/node/symlinks.js +134 -0
- package/dist/node/symlinks.js.map +1 -0
- package/dist/symlinks.d.ts +50 -0
- package/dist/symlinks.d.ts.map +1 -0
- package/dist/symlinks.js +108 -0
- package/dist/symlinks.js.map +1 -0
- package/dist/tool/cp.d.ts +6 -2
- package/dist/tool/cp.d.ts.map +1 -1
- package/dist/tool/cp.js +7 -6
- package/dist/tool/cp.js.map +1 -1
- package/dist/tool/index.core.d.ts +8 -6
- package/dist/tool/index.core.d.ts.map +1 -1
- package/dist/tool/index.core.js +11 -7
- package/dist/tool/index.core.js.map +1 -1
- package/dist/tool/index.d.ts +5 -24
- package/dist/tool/index.d.ts.map +1 -1
- package/dist/tool/index.js +47 -14
- package/dist/tool/index.js.map +1 -1
- package/dist/tool/ln.d.ts +17 -8
- package/dist/tool/ln.d.ts.map +1 -1
- package/dist/tool/ln.js +23 -31
- package/dist/tool/ln.js.map +1 -1
- package/dist/tool/rm.d.ts +6 -2
- package/dist/tool/rm.d.ts.map +1 -1
- package/dist/tool/rm.js +7 -6
- package/dist/tool/rm.js.map +1 -1
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/symlinks.d.ts +35 -10
- package/dist/vite/symlinks.d.ts.map +1 -1
- package/dist/vite/symlinks.js +18 -53
- package/dist/vite/symlinks.js.map +1 -1
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -10,10 +10,13 @@ package consumes a single maintained copy instead of carrying its own. Tools are
|
|
|
10
10
|
| Subpath | Contents |
|
|
11
11
|
|-----------------------------|--------------|
|
|
12
12
|
| [@metreeca/rack/tool][tool] | CLI tools |
|
|
13
|
+
| [@metreeca/rack/node][node] | Node hooks |
|
|
13
14
|
| [@metreeca/rack/vite][vite] | Vite plugins |
|
|
14
15
|
|
|
15
16
|
[tool]: https://metreeca.github.io/rack/modules/tool.html
|
|
16
17
|
|
|
18
|
+
[node]: https://metreeca.github.io/rack/modules/node.html
|
|
19
|
+
|
|
17
20
|
[vite]: https://metreeca.github.io/rack/modules/vite.html
|
|
18
21
|
|
|
19
22
|
# Installation
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Metreeca srl
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Node module hooks running a process against the live source of locally linked packages, with no rebuild.
|
|
18
|
+
*
|
|
19
|
+
* Importing this module registers the {@link symlinks} hooks for the rest of the process, with the `customConditions`
|
|
20
|
+
* of the `tsconfig.json` nearest to the current working directory. Node then resolves locally linked packages to the
|
|
21
|
+
* same sources the IDE does. The module must load before any module it resolves, through the `--import` option given
|
|
22
|
+
* on the command line, in an npm script or in the Node.js run configuration template of the IDE:
|
|
23
|
+
*
|
|
24
|
+
* ```shell
|
|
25
|
+
* node --import @metreeca/rack/node src/index.ts
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Plain `node` runs TypeScript sources through its built-in type stripping, which supports erasable syntax only.
|
|
29
|
+
* Sources using `enum`, `namespace` or parameter properties need a loader such as `tsx` registered ahead of the
|
|
30
|
+
* hooks, as in `node --import tsx --import @metreeca/rack/node` or `tsx --import @metreeca/rack/node`.
|
|
31
|
+
*
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
import { registerHooks } from "node:module";
|
|
35
|
+
import { customConditions, symlinks } from "./symlinks.js";
|
|
36
|
+
export { symlinks, customConditions };
|
|
37
|
+
registerHooks(symlinks({ conditions: customConditions(process.cwd()) }));
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AAGtC,aAAa,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ResolveHookSync } from "node:module";
|
|
2
|
+
/**
|
|
3
|
+
* Creates module hooks running a Node process against the live source of locally linked packages, with no rebuild.
|
|
4
|
+
*
|
|
5
|
+
* Imports of workspace members and of sibling checkouts linked into `node_modules` resolve to their TypeScript source,
|
|
6
|
+
* which Node runs through its built-in type stripping, so edits are picked up as they are saved. Packages installed
|
|
7
|
+
* from the registry keep resolving to their build, as do packages whose `exports` name none of the conditions.
|
|
8
|
+
*
|
|
9
|
+
* Packages are looked up as Node does, from the `node_modules` nearest to the importer up through its ancestors;
|
|
10
|
+
* imports with no importer start from the root. A linked checkout importing a package the project links as well
|
|
11
|
+
* loads the linked source rather than the registry copy it installs for itself, so the two never diverge.
|
|
12
|
+
*
|
|
13
|
+
* The process loads a single copy of every linked package and of each of their `peerDependencies`, as libraries
|
|
14
|
+
* holding module state such as Preact hooks require. Those packages resolve from the root, whatever copies a linked
|
|
15
|
+
* checkout installs for itself, and from the importer only where the root does not install them. Other dependencies
|
|
16
|
+
* of a linked package resolve from its checkout, as Node does, since the process loads no other copy of them.
|
|
17
|
+
*
|
|
18
|
+
* Relative imports of a `.js` file from a TypeScript module, as `NodeNext` module resolution requires, resolve to the
|
|
19
|
+
* `.ts` file next to it when the `.js` file does not exist. Sources compiled to `dist` thus run from `src` under plain
|
|
20
|
+
* `node`, with no loader such as `tsx`. Where such a loader is registered as well, the imports it resolves are left
|
|
21
|
+
* to it, and the hooks only add the resolution of linked packages to their TypeScript source.
|
|
22
|
+
*
|
|
23
|
+
* The hooks take effect once registered with `registerHooks` from `node:module`, before the first import they have
|
|
24
|
+
* to resolve, typically from a module loaded with `--import`. Importing `@metreeca/rack/node` registers them with the
|
|
25
|
+
* conditions of the `tsconfig.json` nearest to the current working directory, with no setup module to write.
|
|
26
|
+
*
|
|
27
|
+
* @param options The resolution settings
|
|
28
|
+
*
|
|
29
|
+
* @returns Synchronous module hooks resolving linked imports to source, deduplicating linked packages and their
|
|
30
|
+
* peers, mapping `.js` relative imports to their `.ts` sources, and leaving all other imports to the default resolution
|
|
31
|
+
*/
|
|
32
|
+
export declare function symlinks({ conditions, root }: {
|
|
33
|
+
/**
|
|
34
|
+
* The export conditions mapping each subpath to its source file, as listed in `customConditions`; where a package
|
|
35
|
+
* matches more than one, the key order of its own `exports` decides which applies.
|
|
36
|
+
*/
|
|
37
|
+
conditions: string | readonly string[];
|
|
38
|
+
/**
|
|
39
|
+
* The project root the linked packages and their peers resolve from; defaults to the current working directory.
|
|
40
|
+
*/
|
|
41
|
+
root?: string;
|
|
42
|
+
}): Readonly<{
|
|
43
|
+
resolve: ResolveHookSync;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Reads the custom export conditions a project asks its TypeScript language service to resolve.
|
|
47
|
+
*
|
|
48
|
+
* @param directory The directory the search for `tsconfig.json` starts from, walking up through its ancestors
|
|
49
|
+
*
|
|
50
|
+
* @returns The `customConditions` of the nearest `tsconfig.json`, read through its `extends` chain where it declares
|
|
51
|
+
* none of its own; empty if no configuration is found or none declares any, so that linked packages resolve to `dist`
|
|
52
|
+
*/
|
|
53
|
+
export declare function customConditions(directory: string): readonly string[];
|
|
54
|
+
//# sourceMappingURL=symlinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.d.ts","sourceRoot":"","sources":["../../src/node/symlinks.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAOnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,QAAQ,CAAC,EAExB,UAAU,EACV,IAAoB,EAEpB,EAAE;IAEF;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAEtC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CAEb,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC,CAgGzC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CA2BrE"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Metreeca srl
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
17
|
+
import { dirname, join, resolve } from "node:path";
|
|
18
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
+
import { exports } from "resolve.exports";
|
|
20
|
+
import { bare, lookup, manifest, shared } from "../symlinks.js";
|
|
21
|
+
/**
|
|
22
|
+
* Creates module hooks running a Node process against the live source of locally linked packages, with no rebuild.
|
|
23
|
+
*
|
|
24
|
+
* Imports of workspace members and of sibling checkouts linked into `node_modules` resolve to their TypeScript source,
|
|
25
|
+
* which Node runs through its built-in type stripping, so edits are picked up as they are saved. Packages installed
|
|
26
|
+
* from the registry keep resolving to their build, as do packages whose `exports` name none of the conditions.
|
|
27
|
+
*
|
|
28
|
+
* Packages are looked up as Node does, from the `node_modules` nearest to the importer up through its ancestors;
|
|
29
|
+
* imports with no importer start from the root. A linked checkout importing a package the project links as well
|
|
30
|
+
* loads the linked source rather than the registry copy it installs for itself, so the two never diverge.
|
|
31
|
+
*
|
|
32
|
+
* The process loads a single copy of every linked package and of each of their `peerDependencies`, as libraries
|
|
33
|
+
* holding module state such as Preact hooks require. Those packages resolve from the root, whatever copies a linked
|
|
34
|
+
* checkout installs for itself, and from the importer only where the root does not install them. Other dependencies
|
|
35
|
+
* of a linked package resolve from its checkout, as Node does, since the process loads no other copy of them.
|
|
36
|
+
*
|
|
37
|
+
* Relative imports of a `.js` file from a TypeScript module, as `NodeNext` module resolution requires, resolve to the
|
|
38
|
+
* `.ts` file next to it when the `.js` file does not exist. Sources compiled to `dist` thus run from `src` under plain
|
|
39
|
+
* `node`, with no loader such as `tsx`. Where such a loader is registered as well, the imports it resolves are left
|
|
40
|
+
* to it, and the hooks only add the resolution of linked packages to their TypeScript source.
|
|
41
|
+
*
|
|
42
|
+
* The hooks take effect once registered with `registerHooks` from `node:module`, before the first import they have
|
|
43
|
+
* to resolve, typically from a module loaded with `--import`. Importing `@metreeca/rack/node` registers them with the
|
|
44
|
+
* conditions of the `tsconfig.json` nearest to the current working directory, with no setup module to write.
|
|
45
|
+
*
|
|
46
|
+
* @param options The resolution settings
|
|
47
|
+
*
|
|
48
|
+
* @returns Synchronous module hooks resolving linked imports to source, deduplicating linked packages and their
|
|
49
|
+
* peers, mapping `.js` relative imports to their `.ts` sources, and leaving all other imports to the default resolution
|
|
50
|
+
*/
|
|
51
|
+
export function symlinks({ conditions, root = process.cwd() }) {
|
|
52
|
+
const $conditions = typeof conditions === "string" ? [conditions] : conditions;
|
|
53
|
+
const names = new Set(shared(root)); // the packages resolved from the root, whoever imports them
|
|
54
|
+
return {
|
|
55
|
+
resolve(specifier, context, next) {
|
|
56
|
+
const { parentURL } = context;
|
|
57
|
+
const parent = parentURL?.startsWith("file:") ? fileURLToPath(parentURL) : undefined;
|
|
58
|
+
const base = parent === undefined ? root : dirname(parent);
|
|
59
|
+
const name = bare(specifier);
|
|
60
|
+
const link = name === undefined ? undefined : lookup(name, base) ?? lookup(name, root);
|
|
61
|
+
if (link !== undefined) {
|
|
62
|
+
const path = realpathSync(link);
|
|
63
|
+
const target = source(path, specifier, context.conditions);
|
|
64
|
+
return target === undefined
|
|
65
|
+
? next(specifier, context) // subpath not exported or excluded by a null target: node reports it
|
|
66
|
+
: { url: pathToFileURL(resolve(path, target)).href, shortCircuit: true };
|
|
67
|
+
}
|
|
68
|
+
else if (name !== undefined && names.has(name)) {
|
|
69
|
+
return rooted(specifier, context, next);
|
|
70
|
+
}
|
|
71
|
+
else if (parent?.endsWith(".ts") && /^\.\.?\/.*\.js$/.test(specifier)) {
|
|
72
|
+
return sibling(specifier, context, next, resolve(base, specifier).replace(/\.js$/, ".ts"));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return next(specifier, context);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
function source(path, specifier, conditions) {
|
|
80
|
+
try {
|
|
81
|
+
return exports(manifest(path), specifier, {
|
|
82
|
+
conditions: $conditions,
|
|
83
|
+
require: conditions.includes("require")
|
|
84
|
+
})?.[0];
|
|
85
|
+
}
|
|
86
|
+
catch { // subpath not exported or excluded by a null target
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function rooted(specifier, context, next) {
|
|
91
|
+
try {
|
|
92
|
+
return next(specifier, { ...context, parentURL: pathToFileURL(join(root, "package.json")).href });
|
|
93
|
+
}
|
|
94
|
+
catch { // not installed by the root: falls back to the importer
|
|
95
|
+
return next(specifier, context);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function sibling(specifier, context, next, path) {
|
|
99
|
+
try {
|
|
100
|
+
return next(specifier, context);
|
|
101
|
+
}
|
|
102
|
+
catch (error) { // the .js file does not exist: a .ts file next to it is the source
|
|
103
|
+
if (existsSync(path)) {
|
|
104
|
+
return { url: pathToFileURL(path).href, shortCircuit: true };
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Reads the custom export conditions a project asks its TypeScript language service to resolve.
|
|
114
|
+
*
|
|
115
|
+
* @param directory The directory the search for `tsconfig.json` starts from, walking up through its ancestors
|
|
116
|
+
*
|
|
117
|
+
* @returns The `customConditions` of the nearest `tsconfig.json`, read through its `extends` chain where it declares
|
|
118
|
+
* none of its own; empty if no configuration is found or none declares any, so that linked packages resolve to `dist`
|
|
119
|
+
*/
|
|
120
|
+
export function customConditions(directory) {
|
|
121
|
+
const config = join(directory, "tsconfig.json");
|
|
122
|
+
const parent = dirname(directory);
|
|
123
|
+
return existsSync(config) ? declared(config)
|
|
124
|
+
: parent === directory ? []
|
|
125
|
+
: customConditions(parent);
|
|
126
|
+
function declared(config) {
|
|
127
|
+
const { extends: base, compilerOptions } = JSON.parse(readFileSync(config, "utf8")); // trusted local file, not validated
|
|
128
|
+
const conditions = compilerOptions?.customConditions;
|
|
129
|
+
return conditions !== undefined ? conditions
|
|
130
|
+
: base !== undefined && existsSync(resolve(dirname(config), base)) ? declared(resolve(dirname(config), base))
|
|
131
|
+
: [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=symlinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.js","sourceRoot":"","sources":["../../src/node/symlinks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Package } from "resolve.exports";
|
|
2
|
+
/**
|
|
3
|
+
* The package manifest fields the linked package resolvers and the `ln` command rely on.
|
|
4
|
+
*/
|
|
5
|
+
export type Manifest = Package & Readonly<{
|
|
6
|
+
bin?: string | Readonly<Record<string, string>>;
|
|
7
|
+
workspaces?: readonly string[];
|
|
8
|
+
dependencies?: Readonly<Record<string, string>>;
|
|
9
|
+
devDependencies?: Readonly<Record<string, string>>;
|
|
10
|
+
peerDependencies?: Readonly<Record<string, string>>;
|
|
11
|
+
optionalDependencies?: Readonly<Record<string, string>>;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Matches the package name a module specifier starts with.
|
|
15
|
+
*
|
|
16
|
+
* @param specifier The module specifier to match
|
|
17
|
+
*
|
|
18
|
+
* @returns The scoped or bare package name at the start of `specifier`, subpath excluded; `undefined` for specifiers
|
|
19
|
+
* that no `node_modules` lookup can resolve: relative and absolute paths, URLs, `node:` builtins and virtual module
|
|
20
|
+
* ids prefixed with `\0`, which bundler plugins reserve for modules with no file behind them
|
|
21
|
+
*/
|
|
22
|
+
export declare function bare(specifier: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Looks a package up as Node does, from the `node_modules` nearest to a directory up through its ancestors.
|
|
25
|
+
*
|
|
26
|
+
* @param name The name of the package to look up
|
|
27
|
+
* @param directory The directory the lookup starts from
|
|
28
|
+
*
|
|
29
|
+
* @returns The path of the `node_modules` entry of `name`, if it is a symbolic link; `undefined` if the package is
|
|
30
|
+
* not found or is an installed copy, which the default resolution handles as it does for any registry package
|
|
31
|
+
*/
|
|
32
|
+
export declare function lookup(name: string, directory: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Lists the packages a project loads a single copy of.
|
|
35
|
+
*
|
|
36
|
+
* @param root The project root
|
|
37
|
+
*
|
|
38
|
+
* @returns The names of the packages linked into the `node_modules` of `root`, of its ancestors and of its workspace
|
|
39
|
+
* members, along with the names of their `peerDependencies`, without duplicates
|
|
40
|
+
*/
|
|
41
|
+
export declare function shared(root: string): readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* Reads a package manifest.
|
|
44
|
+
*
|
|
45
|
+
* @param path The path of the package folder
|
|
46
|
+
*
|
|
47
|
+
* @returns The manifest of the package at `path`, read once per process for each distinct `path`
|
|
48
|
+
*/
|
|
49
|
+
export declare function manifest(path: string): Manifest;
|
|
50
|
+
//# sourceMappingURL=symlinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.d.ts","sourceRoot":"","sources":["../src/symlinks.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAW/C;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IAEzC,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/C,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAE9B,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/C,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClD,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD,oBAAoB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAEvD,CAAC,CAAC;AAKH;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ1E;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CA6CtD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAkB/C"}
|
package/dist/symlinks.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Metreeca srl
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, globSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
18
|
+
import { join, parse, relative, sep } from "node:path";
|
|
19
|
+
/**
|
|
20
|
+
* The package manifests read by {@link manifest}, keyed by package folder path and retained for the process lifetime.
|
|
21
|
+
*/
|
|
22
|
+
const manifests = new Map();
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
/**
|
|
25
|
+
* Matches the package name a module specifier starts with.
|
|
26
|
+
*
|
|
27
|
+
* @param specifier The module specifier to match
|
|
28
|
+
*
|
|
29
|
+
* @returns The scoped or bare package name at the start of `specifier`, subpath excluded; `undefined` for specifiers
|
|
30
|
+
* that no `node_modules` lookup can resolve: relative and absolute paths, URLs, `node:` builtins and virtual module
|
|
31
|
+
* ids prefixed with `\0`, which bundler plugins reserve for modules with no file behind them
|
|
32
|
+
*/
|
|
33
|
+
export function bare(specifier) {
|
|
34
|
+
return specifier.match(/^(?![./\0]|[a-z]+:)(@[^/]+\/)?[^/]+/i)?.[0];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Looks a package up as Node does, from the `node_modules` nearest to a directory up through its ancestors.
|
|
38
|
+
*
|
|
39
|
+
* @param name The name of the package to look up
|
|
40
|
+
* @param directory The directory the lookup starts from
|
|
41
|
+
*
|
|
42
|
+
* @returns The path of the `node_modules` entry of `name`, if it is a symbolic link; `undefined` if the package is
|
|
43
|
+
* not found or is an installed copy, which the default resolution handles as it does for any registry package
|
|
44
|
+
*/
|
|
45
|
+
export function lookup(name, directory) {
|
|
46
|
+
const path = (createRequire(join(directory, sep)).resolve.paths(name) ?? []) // trailing separator: a folder, not a file
|
|
47
|
+
.map(modules => join(modules, name))
|
|
48
|
+
.find(candidate => lstatSync(candidate, { throwIfNoEntry: false }));
|
|
49
|
+
return path !== undefined && lstatSync(path).isSymbolicLink() ? path : undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Lists the packages a project loads a single copy of.
|
|
53
|
+
*
|
|
54
|
+
* @param root The project root
|
|
55
|
+
*
|
|
56
|
+
* @returns The names of the packages linked into the `node_modules` of `root`, of its ancestors and of its workspace
|
|
57
|
+
* members, along with the names of their `peerDependencies`, without duplicates
|
|
58
|
+
*/
|
|
59
|
+
export function shared(root) {
|
|
60
|
+
return [...new Set(links().flatMap(link => {
|
|
61
|
+
const { name, peerDependencies } = manifest(realpathSync(link));
|
|
62
|
+
return [name, ...Object.keys(peerDependencies ?? {})];
|
|
63
|
+
}))];
|
|
64
|
+
function links() {
|
|
65
|
+
return [...ancestors(), ...members()]
|
|
66
|
+
.map(directory => join(directory, "node_modules"))
|
|
67
|
+
.flatMap(modules => children(modules)
|
|
68
|
+
.filter(entry => !entry.startsWith(".")) // hidden entries such as .bin
|
|
69
|
+
.flatMap(entry => entry.startsWith("@")
|
|
70
|
+
? children(join(modules, entry)).map(name => join(modules, entry, name))
|
|
71
|
+
: [join(modules, entry)])
|
|
72
|
+
.filter(entry => lstatSync(entry).isSymbolicLink()));
|
|
73
|
+
}
|
|
74
|
+
function ancestors() {
|
|
75
|
+
const { root: base } = parse(root);
|
|
76
|
+
const segments = relative(base, root).split(sep).filter(Boolean);
|
|
77
|
+
return segments.map((_, index) => join(base, ...segments.slice(0, segments.length - index)));
|
|
78
|
+
}
|
|
79
|
+
function members() {
|
|
80
|
+
return existsSync(join(root, "package.json"))
|
|
81
|
+
? (manifest(root).workspaces ?? [])
|
|
82
|
+
.flatMap(pattern => globSync(pattern, { cwd: root }))
|
|
83
|
+
.map(member => join(root, member))
|
|
84
|
+
: [];
|
|
85
|
+
}
|
|
86
|
+
function children(path) {
|
|
87
|
+
return statSync(path, { throwIfNoEntry: false })?.isDirectory() ? readdirSync(path) : [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Reads a package manifest.
|
|
92
|
+
*
|
|
93
|
+
* @param path The path of the package folder
|
|
94
|
+
*
|
|
95
|
+
* @returns The manifest of the package at `path`, read once per process for each distinct `path`
|
|
96
|
+
*/
|
|
97
|
+
export function manifest(path) {
|
|
98
|
+
const cached = manifests.get(path);
|
|
99
|
+
if (cached === undefined) {
|
|
100
|
+
const loaded = JSON.parse(readFileSync(join(path, "package.json"), "utf8")); // trusted local file
|
|
101
|
+
manifests.set(path, loaded);
|
|
102
|
+
return loaded;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return cached;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=symlinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.js","sourceRoot":"","sources":["../src/symlinks.ts"],"names":[],"mappings":"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"}
|
package/dist/tool/cp.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Copies the files matching a set of glob patterns into a target directory, keeping their relative layout.
|
|
4
3
|
*
|
|
4
|
+
* Runs on the command line as `ush cp <target> <pattern>...`.
|
|
5
|
+
*
|
|
5
6
|
* Each match lands under `target` at its path relative to the base of its pattern, that is the leading segments free
|
|
6
7
|
* of glob characters: `src/**\/*.css` copies `src/a/b.css` to `<target>/a/b.css`, and a plain file path copies the
|
|
7
8
|
* file to the top of `target`. Missing directories are created and existing files are overwritten.
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
* Shipping stylesheets next to the compiled modules, from an npm script:
|
|
16
17
|
*
|
|
17
18
|
* ```shell
|
|
18
|
-
* cp dist 'src/**\/*.css'
|
|
19
|
+
* ush cp dist 'src/**\/*.css'
|
|
19
20
|
* ```
|
|
20
21
|
*
|
|
21
22
|
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
|
@@ -27,4 +28,7 @@
|
|
|
27
28
|
* @throws Error If a pattern matches no file, in which case no file is copied for any of the patterns
|
|
28
29
|
*/
|
|
29
30
|
export declare function cp(target: string, ...patterns: readonly string[]): void;
|
|
31
|
+
export declare namespace cp {
|
|
32
|
+
var usage: string;
|
|
33
|
+
}
|
|
30
34
|
//# sourceMappingURL=cp.d.ts.map
|
package/dist/tool/cp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cp.d.ts","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cp.d.ts","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAiCvE;yBAjCe,EAAE"}
|
package/dist/tool/cp.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/*
|
|
3
2
|
* Copyright © 2026 Metreeca srl
|
|
4
3
|
*
|
|
@@ -16,14 +15,16 @@
|
|
|
16
15
|
*/
|
|
17
16
|
import { copyFileSync, globSync, mkdirSync, statSync } from "node:fs";
|
|
18
17
|
import { dirname, join, relative } from "node:path";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The command line synopsis of {@link cp}, printed by `ush cp --help`.
|
|
20
|
+
*/
|
|
21
|
+
cp.usage = "<target> <pattern>...";
|
|
23
22
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
24
23
|
/**
|
|
25
24
|
* Copies the files matching a set of glob patterns into a target directory, keeping their relative layout.
|
|
26
25
|
*
|
|
26
|
+
* Runs on the command line as `ush cp <target> <pattern>...`.
|
|
27
|
+
*
|
|
27
28
|
* Each match lands under `target` at its path relative to the base of its pattern, that is the leading segments free
|
|
28
29
|
* of glob characters: `src/**\/*.css` copies `src/a/b.css` to `<target>/a/b.css`, and a plain file path copies the
|
|
29
30
|
* file to the top of `target`. Missing directories are created and existing files are overwritten.
|
|
@@ -37,7 +38,7 @@ if (import.meta.main) {
|
|
|
37
38
|
* Shipping stylesheets next to the compiled modules, from an npm script:
|
|
38
39
|
*
|
|
39
40
|
* ```shell
|
|
40
|
-
* cp dist 'src/**\/*.css'
|
|
41
|
+
* ush cp dist 'src/**\/*.css'
|
|
41
42
|
* ```
|
|
42
43
|
*
|
|
43
44
|
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
package/dist/tool/cp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cp.js","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cp.js","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGpD;;GAEG;AACH,EAAE,CAAC,KAAK,GAAG,uBAAuB,CAAC;AAGnC,wHAAwH;AAExH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,EAAE,CAAC,MAAc,EAAE,GAAG,QAA2B;IAEhE,IAAK,CAAC,MAAM,EAAG,CAAC;QACf,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC/C,CAAC;IAED,IAAK,CAAC,QAAQ,CAAC,MAAM,EAAG,CAAC;QACxB,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAChD,CAAC;IAED,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QAC5D,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAGH,SAAS,MAAM,CAAC,OAAe;QAE9B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAE9E,IAAK,CAAC,OAAO,CAAC,MAAM,EAAG,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;QAEpF,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE/F,CAAC;AAEF,CAAC"}
|
|
@@ -4,15 +4,17 @@
|
|
|
4
4
|
* The tool receives the operands as its arguments, with `undefined` in place of a missing one, and throws a
|
|
5
5
|
* `TypeError` on a command line it cannot act on, so that the error message also points to the help.
|
|
6
6
|
*
|
|
7
|
-
* `--help` and `-h` print the usage line to stdout and skip the tool. Errors go to
|
|
8
|
-
* usage errors, including unrecognized options, also point to `--help`.
|
|
7
|
+
* `--help` and `-h` print the usage line to stdout, followed by the description if any, and skip the tool. Errors go to
|
|
8
|
+
* stderr prefixed with the command name; usage errors, including unrecognized options, also point to `--help`.
|
|
9
|
+
* Operands starting with `-` follow `--`, which ends option parsing as on any GNU command line.
|
|
9
10
|
*
|
|
10
11
|
* @param tool The tool to run on the operands
|
|
11
|
-
* @param help The operand synopsis printed by `--help` after the
|
|
12
|
-
* @param name The
|
|
13
|
-
* @param args The command line arguments after the
|
|
12
|
+
* @param help The operand synopsis printed by `--help` after the command name, such as `<target> <pattern>...`
|
|
13
|
+
* @param name The command prefixing messages, as in `ush cp`; defaults to the bare script name, else the `tool` name
|
|
14
|
+
* @param args The command line arguments after the command name; defaults to those the process was launched with
|
|
15
|
+
* @param description The text printed by `--help` below the usage line, such as a list of subcommands
|
|
14
16
|
*
|
|
15
17
|
* @returns The process exit status: `0` on success, `1` on failure
|
|
16
18
|
*/
|
|
17
|
-
export declare function cli(tool: (...args: string[]) => void, help: string, name?: string, args?: readonly string[]): number;
|
|
19
|
+
export declare function cli(tool: (...args: string[]) => void, help: string, name?: string, args?: readonly string[], description?: string): number;
|
|
18
20
|
//# sourceMappingURL=index.core.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.core.d.ts","sourceRoot":"","sources":["../../src/tool/index.core.ts"],"names":[],"mappings":"AAoBA
|
|
1
|
+
{"version":3,"file":"index.core.d.ts","sourceRoot":"","sources":["../../src/tool/index.core.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,GAAG,CAClB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,EACjC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAgF,EACtF,IAAI,GAAE,SAAS,MAAM,EAA0B,EAC/C,WAAW,CAAC,EAAE,MAAM,GAClB,MAAM,CAuDR"}
|
package/dist/tool/index.core.js
CHANGED
|
@@ -21,17 +21,19 @@ import { parseArgs } from "node:util";
|
|
|
21
21
|
* The tool receives the operands as its arguments, with `undefined` in place of a missing one, and throws a
|
|
22
22
|
* `TypeError` on a command line it cannot act on, so that the error message also points to the help.
|
|
23
23
|
*
|
|
24
|
-
* `--help` and `-h` print the usage line to stdout and skip the tool. Errors go to
|
|
25
|
-
* usage errors, including unrecognized options, also point to `--help`.
|
|
24
|
+
* `--help` and `-h` print the usage line to stdout, followed by the description if any, and skip the tool. Errors go to
|
|
25
|
+
* stderr prefixed with the command name; usage errors, including unrecognized options, also point to `--help`.
|
|
26
|
+
* Operands starting with `-` follow `--`, which ends option parsing as on any GNU command line.
|
|
26
27
|
*
|
|
27
28
|
* @param tool The tool to run on the operands
|
|
28
|
-
* @param help The operand synopsis printed by `--help` after the
|
|
29
|
-
* @param name The
|
|
30
|
-
* @param args The command line arguments after the
|
|
29
|
+
* @param help The operand synopsis printed by `--help` after the command name, such as `<target> <pattern>...`
|
|
30
|
+
* @param name The command prefixing messages, as in `ush cp`; defaults to the bare script name, else the `tool` name
|
|
31
|
+
* @param args The command line arguments after the command name; defaults to those the process was launched with
|
|
32
|
+
* @param description The text printed by `--help` below the usage line, such as a list of subcommands
|
|
31
33
|
*
|
|
32
34
|
* @returns The process exit status: `0` on success, `1` on failure
|
|
33
35
|
*/
|
|
34
|
-
export function cli(tool, help, name = process.argv[1] !== undefined ? parse(process.argv[1]).name : tool.name, args = process.argv.slice(2)) {
|
|
36
|
+
export function cli(tool, help, name = process.argv[1] !== undefined ? parse(process.argv[1]).name : tool.name, args = process.argv.slice(2), description) {
|
|
35
37
|
const { values, positionals, tokens } = parseArgs({
|
|
36
38
|
args: [...args],
|
|
37
39
|
options: { help: { type: "boolean", short: "h" } },
|
|
@@ -44,7 +46,9 @@ export function cli(tool, help, name = process.argv[1] !== undefined ? parse(pro
|
|
|
44
46
|
return fail(`unrecognized option <${unknown.rawName}>`, true);
|
|
45
47
|
}
|
|
46
48
|
else if (values.help) {
|
|
47
|
-
console.log(
|
|
49
|
+
console.log(description === undefined
|
|
50
|
+
? `Usage: ${name} ${help}`
|
|
51
|
+
: `Usage: ${name} ${help}\n\n${description}`);
|
|
48
52
|
return 0;
|
|
49
53
|
}
|
|
50
54
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.core.js","sourceRoot":"","sources":["../../src/tool/index.core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC
|
|
1
|
+
{"version":3,"file":"index.core.js","sourceRoot":"","sources":["../../src/tool/index.core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,GAAG,CAClB,IAAiC,EACjC,IAAY,EACZ,OAAe,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EACtF,OAA0B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAC/C,WAAoB;IAGpB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACjD,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAClD,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE,KAAK,EAAE,+DAA+D;QAC9E,MAAM,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CACnC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAChD,CAAC;IAEF,IAAK,OAAO,EAAE,IAAI,KAAK,QAAQ,EAAG,CAAC;QAElC,OAAO,IAAI,CAAC,wBAAwB,OAAO,CAAC,OAAO,GAAG,EAAE,IAAI,CAAC,CAAC;IAE/D,CAAC;SAAM,IAAK,MAAM,CAAC,IAAI,EAAG,CAAC;QAE1B,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,SAAS;YACpC,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE;YAC1B,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,OAAO,WAAW,EAAE,CAC5C,CAAC;QAEF,OAAO,CAAC,CAAC;IAEV,CAAC;SAAM,CAAC;QAEP,IAAI,CAAC;YAEJ,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;YAErB,OAAO,CAAC,CAAC;QAEV,CAAC;QAAC,OAAQ,KAAK,EAAG,CAAC;YAElB,OAAO,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,YAAY,SAAS,CAAC,CAAC;QAEjG,CAAC;IAEF,CAAC;IAGD,SAAS,IAAI,CAAC,OAAe,EAAE,KAAc;QAE5C,OAAO,CAAC,KAAK,CAAC,KAAK;YAClB,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,UAAU,IAAI,gCAAgC;YACnE,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CACvB,CAAC;QAEF,OAAO,CAAC,CAAC;IAEV,CAAC;AAEF,CAAC"}
|
package/dist/tool/index.d.ts
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
* Every tool lists the paths it acts on to stdout, one per line prefixed with the tool name, as in `cp src/index.css`,
|
|
8
|
-
* so that a script log shows what changed on disk and which command changed it. It prints its usage with `--help`, and
|
|
9
|
-
* exits with a non-zero status after printing an error message to stderr, prefixed with the tool name as well.
|
|
10
|
-
*
|
|
11
|
-
* | Command | Purpose |
|
|
12
|
-
* |---------------------------------------|--------------------------------------------------------------------------|
|
|
13
|
-
* | {@link cp `cp <target> <pattern>...`} | Copies files matching glob patterns to a directory, keeping their layout |
|
|
14
|
-
* | {@link rm `rm <pattern>...`} | Removes files and directories matching glob patterns, with their content |
|
|
15
|
-
* | {@link ln `ln [<deps>]...`} | Links dependencies to their local checkouts instead of registry releases |
|
|
16
|
-
*
|
|
17
|
-
* Build scripts can call the same tools as functions, passing the command operands as arguments: a function throws on
|
|
18
|
-
* failure instead of printing an error message to stderr and setting a non-zero exit status for the process.
|
|
19
|
-
*
|
|
20
|
-
* @module
|
|
21
|
-
*/
|
|
22
|
-
export { cp } from "./cp.js";
|
|
23
|
-
export { ln } from "./ln.js";
|
|
24
|
-
export { rm } from "./rm.js";
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cp } from "./cp.js";
|
|
3
|
+
import { ln } from "./ln.js";
|
|
4
|
+
import { rm } from "./rm.js";
|
|
5
|
+
export { cp, ln, rm };
|
|
25
6
|
//# sourceMappingURL=index.d.ts.map
|