@metreeca/rack 0.1.0 → 0.3.0
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 +11 -6
- package/dist/tool/cp.d.ts +30 -0
- package/dist/tool/cp.d.ts.map +1 -0
- package/dist/tool/cp.js +74 -0
- package/dist/tool/cp.js.map +1 -0
- package/dist/tool/index.core.d.ts +18 -0
- package/dist/tool/index.core.d.ts.map +1 -0
- package/dist/tool/index.core.js +66 -0
- package/dist/tool/index.core.js.map +1 -0
- package/dist/tool/index.d.ts +24 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +39 -0
- package/dist/tool/index.js.map +1 -0
- package/dist/tool/ln.d.ts +47 -0
- package/dist/tool/ln.d.ts.map +1 -0
- package/dist/tool/ln.js +187 -0
- package/dist/tool/ln.js.map +1 -0
- package/dist/tool/rm.d.ts +25 -0
- package/dist/tool/rm.d.ts.map +1 -0
- package/dist/tool/rm.js +57 -0
- package/dist/tool/rm.js.map +1 -0
- package/dist/vite/index.d.ts +7 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +10 -1
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/symlinks.d.ts +48 -0
- package/dist/vite/symlinks.d.ts.map +1 -0
- package/dist/vite/symlinks.js +102 -0
- package/dist/vite/symlinks.js.map +1 -0
- package/package.json +18 -4
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -17
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@metreeca/rack)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Everyday tooling for npm package development.
|
|
6
6
|
|
|
7
|
-
**@metreeca/rack** collects
|
|
8
|
-
consumes a single maintained copy instead of carrying its own. Tools are grouped by the toolchain they extend:
|
|
7
|
+
**@metreeca/rack** collects build, test and documentation tooling for recurring npm package development needs, so each
|
|
8
|
+
package consumes a single maintained copy instead of carrying its own. Tools are grouped by the toolchain they extend:
|
|
9
9
|
|
|
10
|
-
| Subpath
|
|
11
|
-
|
|
12
|
-
|
|
|
10
|
+
| Subpath | Contents |
|
|
11
|
+
|-----------------------------|--------------|
|
|
12
|
+
| [@metreeca/rack/tool][tool] | CLI tools |
|
|
13
|
+
| [@metreeca/rack/vite][vite] | Vite plugins |
|
|
14
|
+
|
|
15
|
+
[tool]: https://metreeca.github.io/rack/modules/tool.html
|
|
16
|
+
|
|
17
|
+
[vite]: https://metreeca.github.io/rack/modules/vite.html
|
|
13
18
|
|
|
14
19
|
# Installation
|
|
15
20
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copies the files matching a set of glob patterns into a target directory, keeping their relative layout.
|
|
4
|
+
*
|
|
5
|
+
* Each match lands under `target` at its path relative to the base of its pattern, that is the leading segments free
|
|
6
|
+
* of glob characters: `src/**\/*.css` copies `src/a/b.css` to `<target>/a/b.css`, and a plain file path copies the
|
|
7
|
+
* file to the top of `target`. Missing directories are created and existing files are overwritten.
|
|
8
|
+
*
|
|
9
|
+
* Directories matched by a pattern are skipped: only files are copied.
|
|
10
|
+
*
|
|
11
|
+
* Each copied file is printed on stdout, one per line, by its source path as the pattern matched it.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* Shipping stylesheets next to the compiled modules, from an npm script:
|
|
16
|
+
*
|
|
17
|
+
* ```shell
|
|
18
|
+
* cp dist 'src/**\/*.css'
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
|
22
|
+
*
|
|
23
|
+
* @param target The directory receiving the copies
|
|
24
|
+
* @param patterns The glob patterns selecting the files to copy
|
|
25
|
+
*
|
|
26
|
+
* @throws TypeError If `target` is empty or if no pattern is provided
|
|
27
|
+
* @throws Error If a pattern matches no file, in which case no file is copied for any of the patterns
|
|
28
|
+
*/
|
|
29
|
+
export declare function cp(target: string, ...patterns: readonly string[]): void;
|
|
30
|
+
//# sourceMappingURL=cp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cp.d.ts","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":";AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAiCvE"}
|
package/dist/tool/cp.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright © 2026 Metreeca srl
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { copyFileSync, globSync, mkdirSync, statSync } from "node:fs";
|
|
18
|
+
import { dirname, join, relative } from "node:path";
|
|
19
|
+
import { cli } from "./index.core.js";
|
|
20
|
+
if (import.meta.main) {
|
|
21
|
+
process.exitCode = cli(cp, "<target> <pattern>...");
|
|
22
|
+
}
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
/**
|
|
25
|
+
* Copies the files matching a set of glob patterns into a target directory, keeping their relative layout.
|
|
26
|
+
*
|
|
27
|
+
* Each match lands under `target` at its path relative to the base of its pattern, that is the leading segments free
|
|
28
|
+
* of glob characters: `src/**\/*.css` copies `src/a/b.css` to `<target>/a/b.css`, and a plain file path copies the
|
|
29
|
+
* file to the top of `target`. Missing directories are created and existing files are overwritten.
|
|
30
|
+
*
|
|
31
|
+
* Directories matched by a pattern are skipped: only files are copied.
|
|
32
|
+
*
|
|
33
|
+
* Each copied file is printed on stdout, one per line, by its source path as the pattern matched it.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
*
|
|
37
|
+
* Shipping stylesheets next to the compiled modules, from an npm script:
|
|
38
|
+
*
|
|
39
|
+
* ```shell
|
|
40
|
+
* cp dist 'src/**\/*.css'
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
|
44
|
+
*
|
|
45
|
+
* @param target The directory receiving the copies
|
|
46
|
+
* @param patterns The glob patterns selecting the files to copy
|
|
47
|
+
*
|
|
48
|
+
* @throws TypeError If `target` is empty or if no pattern is provided
|
|
49
|
+
* @throws Error If a pattern matches no file, in which case no file is copied for any of the patterns
|
|
50
|
+
*/
|
|
51
|
+
export function cp(target, ...patterns) {
|
|
52
|
+
if (!target) {
|
|
53
|
+
throw new TypeError("missing target operand");
|
|
54
|
+
}
|
|
55
|
+
if (!patterns.length) {
|
|
56
|
+
throw new TypeError("missing pattern operand");
|
|
57
|
+
}
|
|
58
|
+
patterns.flatMap(copies).forEach(({ source, destination }) => {
|
|
59
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
60
|
+
copyFileSync(source, destination);
|
|
61
|
+
console.log(source);
|
|
62
|
+
});
|
|
63
|
+
function copies(pattern) {
|
|
64
|
+
const sources = globSync(pattern).filter(source => statSync(source).isFile());
|
|
65
|
+
if (!sources.length) {
|
|
66
|
+
throw new Error(`unmatched pattern <${pattern}>`);
|
|
67
|
+
}
|
|
68
|
+
const segments = pattern.split("/");
|
|
69
|
+
const wild = segments.findIndex(segment => /[*?[\]{}]/.test(segment));
|
|
70
|
+
const base = wild < 0 ? dirname(pattern) : segments.slice(0, wild).join("/") || ".";
|
|
71
|
+
return sources.map(source => ({ source, destination: join(target, relative(base, source)) }));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=cp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cp.js","sourceRoot":"","sources":["../../src/tool/cp.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;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;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGtC,IAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAG,CAAC;IACxB,OAAO,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,EAAE,uBAAuB,CAAC,CAAC;AACrD,CAAC;AAGD,wHAAwH;AAExH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;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,CAAC,CAAC;IACrB,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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs a tool on a command line, with GNU-style help and error messages.
|
|
3
|
+
*
|
|
4
|
+
* The tool receives the operands as its arguments, with `undefined` in place of a missing one, and throws a
|
|
5
|
+
* `TypeError` on a command line it cannot act on, so that the error message also points to the help.
|
|
6
|
+
*
|
|
7
|
+
* `--help` and `-h` print the usage line to stdout and skip the tool. Errors go to stderr prefixed with the tool name;
|
|
8
|
+
* usage errors, including unrecognized options, also point to `--help`. Operands starting with `-` follow `--`.
|
|
9
|
+
*
|
|
10
|
+
* @param tool The tool to run on the operands
|
|
11
|
+
* @param help The operand synopsis printed by `--help` after the tool name, such as `<target> <pattern>...`
|
|
12
|
+
* @param name The tool name prefixing messages; defaults to the script name without extension, else the `tool` name
|
|
13
|
+
* @param args The command line arguments after the script path; defaults to those the process was launched with
|
|
14
|
+
*
|
|
15
|
+
* @returns The process exit status: `0` on success, `1` on failure
|
|
16
|
+
*/
|
|
17
|
+
export declare function cli(tool: (...args: string[]) => void, help: string, name?: string, args?: readonly string[]): number;
|
|
18
|
+
//# sourceMappingURL=index.core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.core.d.ts","sourceRoot":"","sources":["../../src/tool/index.core.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;GAeG;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,GAC7C,MAAM,CAoDR"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { parse } from "node:path";
|
|
17
|
+
import { parseArgs } from "node:util";
|
|
18
|
+
/**
|
|
19
|
+
* Runs a tool on a command line, with GNU-style help and error messages.
|
|
20
|
+
*
|
|
21
|
+
* The tool receives the operands as its arguments, with `undefined` in place of a missing one, and throws a
|
|
22
|
+
* `TypeError` on a command line it cannot act on, so that the error message also points to the help.
|
|
23
|
+
*
|
|
24
|
+
* `--help` and `-h` print the usage line to stdout and skip the tool. Errors go to stderr prefixed with the tool name;
|
|
25
|
+
* usage errors, including unrecognized options, also point to `--help`. Operands starting with `-` follow `--`.
|
|
26
|
+
*
|
|
27
|
+
* @param tool The tool to run on the operands
|
|
28
|
+
* @param help The operand synopsis printed by `--help` after the tool name, such as `<target> <pattern>...`
|
|
29
|
+
* @param name The tool name prefixing messages; defaults to the script name without extension, else the `tool` name
|
|
30
|
+
* @param args The command line arguments after the script path; defaults to those the process was launched with
|
|
31
|
+
*
|
|
32
|
+
* @returns The process exit status: `0` on success, `1` on failure
|
|
33
|
+
*/
|
|
34
|
+
export function cli(tool, help, name = process.argv[1] !== undefined ? parse(process.argv[1]).name : tool.name, args = process.argv.slice(2)) {
|
|
35
|
+
const { values, positionals, tokens } = parseArgs({
|
|
36
|
+
args: [...args],
|
|
37
|
+
options: { help: { type: "boolean", short: "h" } },
|
|
38
|
+
allowPositionals: true,
|
|
39
|
+
strict: false, // unknown options are rejected below, with a GNU-style message
|
|
40
|
+
tokens: true
|
|
41
|
+
});
|
|
42
|
+
const unknown = tokens.find(token => token.kind === "option" && token.name !== "help");
|
|
43
|
+
if (unknown?.kind === "option") {
|
|
44
|
+
return fail(`unrecognized option <${unknown.rawName}>`, true);
|
|
45
|
+
}
|
|
46
|
+
else if (values.help) {
|
|
47
|
+
console.log(`Usage: ${name} ${help}`);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
try {
|
|
52
|
+
tool(...positionals);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return fail(error instanceof Error ? error.message : String(error), error instanceof TypeError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function fail(message, usage) {
|
|
60
|
+
console.error(usage
|
|
61
|
+
? `${name}: ${message}\nTry '${name} --help' for more information.`
|
|
62
|
+
: `${name}: ${message}`);
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=index.core.js.map
|
|
@@ -0,0 +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;;;;;;;;;;;;;;;GAeG;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;IAG/C,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,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;QAEtC,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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unix-like command line tools for npm package scripts.
|
|
3
|
+
*
|
|
4
|
+
* Each tool is installed as a command alongside the package and behaves the same on every platform and shell. Inside
|
|
5
|
+
* npm scripts, these commands take precedence over system commands with the same name, such as the Unix `cp`.
|
|
6
|
+
*
|
|
7
|
+
* Every tool lists the paths it acts on to stdout, one per line, so that a script log shows what changed on disk. It
|
|
8
|
+
* prints its usage with `--help`, and exits with a non-zero status after printing an error message to stderr.
|
|
9
|
+
*
|
|
10
|
+
* | Command | Purpose |
|
|
11
|
+
* |---------------------------------------|--------------------------------------------------------------------------|
|
|
12
|
+
* | {@link cp `cp <target> <pattern>...`} | Copies files matching glob patterns to a directory, keeping their layout |
|
|
13
|
+
* | {@link rm `rm <pattern>...`} | Removes files and directories matching glob patterns, with their content |
|
|
14
|
+
* | {@link ln `ln [<deps>]...`} | Links dependencies to their local checkouts instead of registry releases |
|
|
15
|
+
*
|
|
16
|
+
* Build scripts can call the same tools as functions, passing the command operands as arguments: a function throws on
|
|
17
|
+
* failure instead of printing an error message to stderr and setting a non-zero exit status for the process.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
export { cp } from "./cp.js";
|
|
22
|
+
export { ln } from "./ln.js";
|
|
23
|
+
export { rm } from "./rm.js";
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool/index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
* Unix-like command line tools for npm package scripts.
|
|
18
|
+
*
|
|
19
|
+
* Each tool is installed as a command alongside the package and behaves the same on every platform and shell. Inside
|
|
20
|
+
* npm scripts, these commands take precedence over system commands with the same name, such as the Unix `cp`.
|
|
21
|
+
*
|
|
22
|
+
* Every tool lists the paths it acts on to stdout, one per line, so that a script log shows what changed on disk. It
|
|
23
|
+
* prints its usage with `--help`, and exits with a non-zero status after printing an error message to stderr.
|
|
24
|
+
*
|
|
25
|
+
* | Command | Purpose |
|
|
26
|
+
* |---------------------------------------|--------------------------------------------------------------------------|
|
|
27
|
+
* | {@link cp `cp <target> <pattern>...`} | Copies files matching glob patterns to a directory, keeping their layout |
|
|
28
|
+
* | {@link rm `rm <pattern>...`} | Removes files and directories matching glob patterns, with their content |
|
|
29
|
+
* | {@link ln `ln [<deps>]...`} | Links dependencies to their local checkouts instead of registry releases |
|
|
30
|
+
*
|
|
31
|
+
* Build scripts can call the same tools as functions, passing the command operands as arguments: a function throws on
|
|
32
|
+
* failure instead of printing an error message to stderr and setting a non-zero exit status for the process.
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
export { cp } from "./cp.js";
|
|
37
|
+
export { ln } from "./ln.js";
|
|
38
|
+
export { rm } from "./rm.js";
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tool/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Links the local checkouts of the dependencies of the package in the current folder into its `node_modules`.
|
|
4
|
+
*
|
|
5
|
+
* The current package, and in a monorepo each of its workspace members, then builds and runs against the working
|
|
6
|
+
* trees of its dependencies instead of their registry releases. Dependencies are matched to checkouts by package name.
|
|
7
|
+
*
|
|
8
|
+
* - **Deps**: each folder searched is either a package or a folder holding packages as direct children; a monorepo
|
|
9
|
+
* found there contributes its workspace members as well, wherever its `workspaces` patterns place them
|
|
10
|
+
* - **Selection**: links every package named in the `dependencies`, `devDependencies`, `peerDependencies` or
|
|
11
|
+
* `optionalDependencies` of the current package or its members. Each linked package then brings in the checkouts
|
|
12
|
+
* named in its own `dependencies`, `peerDependencies` or `optionalDependencies`. A package shared along the chain
|
|
13
|
+
* is thus loaded once from its working tree, never also as a registry copy with distinct classes and symbols
|
|
14
|
+
* - **Copies**: installed copies of a linked package are replaced with links at any depth, both in the current
|
|
15
|
+
* package and in linked checkouts, since a linked package resolves its own imports from its checkout
|
|
16
|
+
* - **Commands**: the `bin` commands of each linked package are linked into the `.bin` folder next to it and made
|
|
17
|
+
* executable, so that npm scripts run the working tree version of a command rather than its registry release
|
|
18
|
+
* - **Precedence**: where a name is found in more than one folder, the folder listed first wins
|
|
19
|
+
* - **Versions**: version ranges are ignored, as a working tree may carry a version that is not released yet
|
|
20
|
+
*
|
|
21
|
+
* Links replace whatever the current package has installed under the same name. Inside a linked checkout, they only
|
|
22
|
+
* replace installed copies and never add new packages. Missing folders are skipped, so the same command runs
|
|
23
|
+
* unchanged on every machine, including machines where some of the named folders hold no local checkouts at all.
|
|
24
|
+
*
|
|
25
|
+
* Each package link is printed on stdout, relative to the current package, such as `node_modules/@scope/name`; links
|
|
26
|
+
* to `bin` commands are created silently, since they follow from the package links already listed.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
*
|
|
30
|
+
* Linking the sibling checkouts of a package after a clean install, from an npm script:
|
|
31
|
+
*
|
|
32
|
+
* ```shell
|
|
33
|
+
* npm ci && ln
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Linking from a folder of local dependencies elsewhere:
|
|
37
|
+
*
|
|
38
|
+
* ```shell
|
|
39
|
+
* npm ci && ln ~/Metreeca/Packages
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @param deps The folders searched for local dependencies, in order of precedence; defaults to the parent folder (`..`)
|
|
43
|
+
*
|
|
44
|
+
* @throws Error If the current folder holds no `package.json`
|
|
45
|
+
*/
|
|
46
|
+
export declare function ln(...deps: readonly string[]): void;
|
|
47
|
+
//# sourceMappingURL=ln.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ln.d.ts","sourceRoot":"","sources":["../../src/tool/ln.ts"],"names":[],"mappings":";AA0CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,EAAE,CAAC,GAAG,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAiLnD"}
|
package/dist/tool/ln.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright © 2026 Metreeca srl
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { chmodSync, existsSync, globSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync } from "node:fs";
|
|
18
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
19
|
+
import { cli } from "./index.core.js";
|
|
20
|
+
if (import.meta.main) {
|
|
21
|
+
process.exitCode = cli(ln, "[<deps>]...");
|
|
22
|
+
}
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
/**
|
|
25
|
+
* Links the local checkouts of the dependencies of the package in the current folder into its `node_modules`.
|
|
26
|
+
*
|
|
27
|
+
* The current package, and in a monorepo each of its workspace members, then builds and runs against the working
|
|
28
|
+
* trees of its dependencies instead of their registry releases. Dependencies are matched to checkouts by package name.
|
|
29
|
+
*
|
|
30
|
+
* - **Deps**: each folder searched is either a package or a folder holding packages as direct children; a monorepo
|
|
31
|
+
* found there contributes its workspace members as well, wherever its `workspaces` patterns place them
|
|
32
|
+
* - **Selection**: links every package named in the `dependencies`, `devDependencies`, `peerDependencies` or
|
|
33
|
+
* `optionalDependencies` of the current package or its members. Each linked package then brings in the checkouts
|
|
34
|
+
* named in its own `dependencies`, `peerDependencies` or `optionalDependencies`. A package shared along the chain
|
|
35
|
+
* is thus loaded once from its working tree, never also as a registry copy with distinct classes and symbols
|
|
36
|
+
* - **Copies**: installed copies of a linked package are replaced with links at any depth, both in the current
|
|
37
|
+
* package and in linked checkouts, since a linked package resolves its own imports from its checkout
|
|
38
|
+
* - **Commands**: the `bin` commands of each linked package are linked into the `.bin` folder next to it and made
|
|
39
|
+
* executable, so that npm scripts run the working tree version of a command rather than its registry release
|
|
40
|
+
* - **Precedence**: where a name is found in more than one folder, the folder listed first wins
|
|
41
|
+
* - **Versions**: version ranges are ignored, as a working tree may carry a version that is not released yet
|
|
42
|
+
*
|
|
43
|
+
* Links replace whatever the current package has installed under the same name. Inside a linked checkout, they only
|
|
44
|
+
* replace installed copies and never add new packages. Missing folders are skipped, so the same command runs
|
|
45
|
+
* unchanged on every machine, including machines where some of the named folders hold no local checkouts at all.
|
|
46
|
+
*
|
|
47
|
+
* Each package link is printed on stdout, relative to the current package, such as `node_modules/@scope/name`; links
|
|
48
|
+
* to `bin` commands are created silently, since they follow from the package links already listed.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
*
|
|
52
|
+
* Linking the sibling checkouts of a package after a clean install, from an npm script:
|
|
53
|
+
*
|
|
54
|
+
* ```shell
|
|
55
|
+
* npm ci && ln
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Linking from a folder of local dependencies elsewhere:
|
|
59
|
+
*
|
|
60
|
+
* ```shell
|
|
61
|
+
* npm ci && ln ~/Metreeca/Packages
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @param deps The folders searched for local dependencies, in order of precedence; defaults to the parent folder (`..`)
|
|
65
|
+
*
|
|
66
|
+
* @throws Error If the current folder holds no `package.json`
|
|
67
|
+
*/
|
|
68
|
+
export function ln(...deps) {
|
|
69
|
+
const home = resolve(".");
|
|
70
|
+
const local = join(home, "package.json");
|
|
71
|
+
if (!existsSync(local)) {
|
|
72
|
+
throw new Error(`missing package manifest <${local}>`);
|
|
73
|
+
}
|
|
74
|
+
const own = packages(home);
|
|
75
|
+
const names = new Set(own.map(path => manifest(path).name));
|
|
76
|
+
const index = new Map((deps.length ? deps : [".."])
|
|
77
|
+
.map(dep => resolve(dep))
|
|
78
|
+
.flatMap(dep => existsSync(join(dep, "package.json"))
|
|
79
|
+
? [dep]
|
|
80
|
+
: children(dep).map(name => join(dep, name)).filter(folder))
|
|
81
|
+
.flatMap(packages)
|
|
82
|
+
.map((path) => [manifest(path).name, path])
|
|
83
|
+
.filter(([name]) => !names.has(name))
|
|
84
|
+
.reverse() // later entries override earlier ones, so that the folder listed first wins
|
|
85
|
+
);
|
|
86
|
+
const linked = closure(own.flatMap(path => dependencies(path, true)));
|
|
87
|
+
const roots = new Set([home, ...[...linked.values()].map(path => checkout(path))]);
|
|
88
|
+
linked.forEach((path, name) => link({ modules: join(home, "node_modules"), name, path }));
|
|
89
|
+
[...roots]
|
|
90
|
+
.flatMap(packages)
|
|
91
|
+
.flatMap(path => copies(join(path, "node_modules")))
|
|
92
|
+
.forEach(link);
|
|
93
|
+
function link({ modules, name, path }) {
|
|
94
|
+
const target = join(modules, name);
|
|
95
|
+
replace(target, path);
|
|
96
|
+
console.log(relative(home, target));
|
|
97
|
+
commands(path, name).forEach(([command, script]) => {
|
|
98
|
+
replace(join(modules, ".bin", command), join(target, script));
|
|
99
|
+
if (existsSync(join(path, script))) {
|
|
100
|
+
chmodSync(join(path, script), 0o755); // as npm does, since builds such as tsc emit scripts without the mode
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function replace(link, target) {
|
|
105
|
+
rmSync(link, { recursive: true, force: true }); // removes links without following them
|
|
106
|
+
mkdirSync(dirname(link), { recursive: true });
|
|
107
|
+
symlinkSync(relative(dirname(link), target), link, "junction"); // type ignored outside Windows
|
|
108
|
+
}
|
|
109
|
+
function commands(path, name) {
|
|
110
|
+
const { bin } = manifest(path);
|
|
111
|
+
if (process.platform === "win32") {
|
|
112
|
+
return []; // npm exposes commands through generated shims rather than links
|
|
113
|
+
}
|
|
114
|
+
else if (typeof bin === "string") {
|
|
115
|
+
return [[name.replace(/^@[^/]+\//, ""), bin]];
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
return Object.entries(bin ?? {});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function copies(modules) {
|
|
122
|
+
return children(modules)
|
|
123
|
+
.filter(name => !name.startsWith("."))
|
|
124
|
+
.flatMap(name => name.startsWith("@")
|
|
125
|
+
? children(join(modules, name)).map(member => `${name}/${member}`)
|
|
126
|
+
: [name])
|
|
127
|
+
.flatMap(name => {
|
|
128
|
+
const copy = join(modules, name);
|
|
129
|
+
const path = linked.get(name);
|
|
130
|
+
if (path !== undefined) {
|
|
131
|
+
return existsSync(copy) && realpathSync(copy) === realpathSync(path) ? [] : [{ modules, name, path }];
|
|
132
|
+
}
|
|
133
|
+
else if (lstatSync(copy).isSymbolicLink()) {
|
|
134
|
+
return []; // a link into another checkout, inspected as a root of its own when linked
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return copies(join(copy, "node_modules"));
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function packages(path) {
|
|
142
|
+
return existsSync(join(path, "package.json")) ? [path, ...(manifest(path).workspaces ?? [])
|
|
143
|
+
.flatMap(pattern => globSync(pattern, { cwd: path }))
|
|
144
|
+
.map(member => join(path, member))
|
|
145
|
+
.filter(member => existsSync(join(member, "package.json")))
|
|
146
|
+
] : [];
|
|
147
|
+
}
|
|
148
|
+
function checkout(path, parent = dirname(path)) {
|
|
149
|
+
if (parent === dirname(parent)) {
|
|
150
|
+
return path;
|
|
151
|
+
}
|
|
152
|
+
else if (existsSync(join(parent, "package.json"))) {
|
|
153
|
+
return packages(parent).includes(path) ? parent : path;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
return checkout(path, dirname(parent));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function children(path) {
|
|
160
|
+
return folder(path) ? readdirSync(path) : [];
|
|
161
|
+
}
|
|
162
|
+
function folder(path) {
|
|
163
|
+
return statSync(path, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
164
|
+
}
|
|
165
|
+
function closure(required, closed = new Map()) {
|
|
166
|
+
const fresh = new Map(required.flatMap((name) => {
|
|
167
|
+
const path = index.get(name);
|
|
168
|
+
return path !== undefined && !closed.has(name) ? [[name, path]] : [];
|
|
169
|
+
}));
|
|
170
|
+
return fresh.size
|
|
171
|
+
? closure([...fresh.values()].flatMap(path => dependencies(path)), new Map([...closed, ...fresh]))
|
|
172
|
+
: closed;
|
|
173
|
+
}
|
|
174
|
+
function dependencies(path, dev = false) {
|
|
175
|
+
const { dependencies, devDependencies, peerDependencies, optionalDependencies } = manifest(path);
|
|
176
|
+
return Object.keys({
|
|
177
|
+
...dependencies,
|
|
178
|
+
...(dev ? devDependencies : {}),
|
|
179
|
+
...peerDependencies,
|
|
180
|
+
...optionalDependencies
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function manifest(path) {
|
|
184
|
+
return JSON.parse(readFileSync(join(path, "package.json"), "utf8")); // trusted local file, not validated
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=ln.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ln.js","sourceRoot":"","sources":["../../src/tool/ln.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Removes the files and directories matching a set of glob patterns, with their whole content.
|
|
4
|
+
*
|
|
5
|
+
* Patterns matching nothing are ignored, so that the same command can run again on an already clean tree.
|
|
6
|
+
*
|
|
7
|
+
* Each removed file or directory is printed on stdout once, relative to the current folder. Matches inside a removed
|
|
8
|
+
* directory are not listed, even where a pattern selects them explicitly, as they go along with their ancestor.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* Removing build outputs, from an npm script:
|
|
13
|
+
*
|
|
14
|
+
* ```shell
|
|
15
|
+
* rm dist docs 'src/**\/*.tsbuildinfo'
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
|
19
|
+
*
|
|
20
|
+
* @param patterns The glob patterns selecting the files and directories to remove
|
|
21
|
+
*
|
|
22
|
+
* @throws TypeError If no pattern is provided
|
|
23
|
+
*/
|
|
24
|
+
export declare function rm(...patterns: readonly string[]): void;
|
|
25
|
+
//# sourceMappingURL=rm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rm.d.ts","sourceRoot":"","sources":["../../src/tool/rm.ts"],"names":[],"mappings":";AA8BA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,EAAE,CAAC,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAcvD"}
|
package/dist/tool/rm.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright © 2026 Metreeca srl
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { globSync, rmSync } from "node:fs";
|
|
18
|
+
import { relative, resolve, sep } from "node:path";
|
|
19
|
+
import { cli } from "./index.core.js";
|
|
20
|
+
if (import.meta.main) {
|
|
21
|
+
process.exitCode = cli(rm, "<pattern>...");
|
|
22
|
+
}
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
/**
|
|
25
|
+
* Removes the files and directories matching a set of glob patterns, with their whole content.
|
|
26
|
+
*
|
|
27
|
+
* Patterns matching nothing are ignored, so that the same command can run again on an already clean tree.
|
|
28
|
+
*
|
|
29
|
+
* Each removed file or directory is printed on stdout once, relative to the current folder. Matches inside a removed
|
|
30
|
+
* directory are not listed, even where a pattern selects them explicitly, as they go along with their ancestor.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
*
|
|
34
|
+
* Removing build outputs, from an npm script:
|
|
35
|
+
*
|
|
36
|
+
* ```shell
|
|
37
|
+
* rm dist docs 'src/**\/*.tsbuildinfo'
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* Patterns are quoted so that they reach the tool unexpanded, as shells differ in their glob support.
|
|
41
|
+
*
|
|
42
|
+
* @param patterns The glob patterns selecting the files and directories to remove
|
|
43
|
+
*
|
|
44
|
+
* @throws TypeError If no pattern is provided
|
|
45
|
+
*/
|
|
46
|
+
export function rm(...patterns) {
|
|
47
|
+
if (!patterns.length) {
|
|
48
|
+
throw new TypeError("missing pattern operand");
|
|
49
|
+
}
|
|
50
|
+
const matches = [...new Set(patterns.flatMap(pattern => globSync(pattern)).map(path => resolve(path)))];
|
|
51
|
+
const paths = matches.filter(path => !matches.some(match => path.startsWith(match + sep))); // gone with ancestors
|
|
52
|
+
paths.forEach(path => {
|
|
53
|
+
rmSync(path, { recursive: true, force: true });
|
|
54
|
+
console.log(relative(".", path));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=rm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rm.js","sourceRoot":"","sources":["../../src/tool/rm.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGtC,IAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAG,CAAC;IACxB,OAAO,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;AAC5C,CAAC;AAGD,wHAAwH;AAExH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,EAAE,CAAC,GAAG,QAA2B;IAEhD,IAAK,CAAC,QAAQ,CAAC,MAAM,EAAG,CAAC;QACxB,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxG,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB;IAElH,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACpB,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AAEJ,CAAC"}
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vite plugins for building and testing
|
|
2
|
+
* Vite plugins for building and testing npm packages.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* | Plugin | Purpose |
|
|
5
|
+
* |------------------|---------------------------------------------------------------------------------|
|
|
6
|
+
* | {@link symlinks} | Runs tests against the live source of locally linked packages, with no rebuild |
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
5
9
|
*/
|
|
6
|
-
export {};
|
|
10
|
+
export { symlinks } from "./symlinks.js";
|
|
7
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/vite/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAgBA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/vite/index.js
CHANGED
|
@@ -13,5 +13,14 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Vite plugins for building and testing npm packages.
|
|
18
|
+
*
|
|
19
|
+
* | Plugin | Purpose |
|
|
20
|
+
* |------------------|---------------------------------------------------------------------------------|
|
|
21
|
+
* | {@link symlinks} | Runs tests against the live source of locally linked packages, with no rebuild |
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
export { symlinks } from "./symlinks.js";
|
|
17
26
|
//# sourceMappingURL=index.js.map
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a plugin running tests 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
|
+
* so edits are picked up as they are saved. Packages installed from the registry keep resolving to their build.
|
|
7
|
+
*
|
|
8
|
+
* Packages are looked up as Node does, from the `node_modules` nearest to the importer up through its ancestors, so
|
|
9
|
+
* links hoisted to a workspace root are found from any member; imports with no importer start from the Vite root.
|
|
10
|
+
*
|
|
11
|
+
* The plugin replaces `resolve.conditions`, which breaks registry packages whose exports name a condition without
|
|
12
|
+
* publishing its target: Vite does not fall back to `default` when that target is missing from the installed files.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
*
|
|
16
|
+
* Linked packages map each exported subpath to its source under a custom condition, next to the build:
|
|
17
|
+
*
|
|
18
|
+
* ```json
|
|
19
|
+
* {
|
|
20
|
+
* "exports": {
|
|
21
|
+
* ".": { "source": "./src/index.ts", "default": "./dist/index.js" }
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The consuming project registers the plugin with the same condition in its Vite or Vitest configuration:
|
|
27
|
+
*
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { symlinks } from "@metreeca/rack/vite";
|
|
30
|
+
* import { defineConfig } from "vitest/config";
|
|
31
|
+
*
|
|
32
|
+
* export default defineConfig({
|
|
33
|
+
* plugins: [symlinks({ conditions: "source" })]
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param options The resolution settings
|
|
38
|
+
*
|
|
39
|
+
* @returns A plugin resolving linked imports to source and leaving all others to the default resolution
|
|
40
|
+
*/
|
|
41
|
+
export declare function symlinks({ conditions }: {
|
|
42
|
+
/**
|
|
43
|
+
* The export conditions mapping each subpath to its source file, as listed in `customConditions`; where a package
|
|
44
|
+
* matches more than one, the key order of its own `exports` decides which applies.
|
|
45
|
+
*/
|
|
46
|
+
conditions: string | readonly string[];
|
|
47
|
+
}): Plugin;
|
|
48
|
+
//# sourceMappingURL=symlinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.d.ts","sourceRoot":"","sources":["../../src/vite/symlinks.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,QAAQ,CAAC,EAExB,UAAU,EAEV,EAAE;IAEF;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;CAEtC,GAAG,MAAM,CAyET"}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
18
|
+
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
19
|
+
import { exports } from "resolve.exports";
|
|
20
|
+
/**
|
|
21
|
+
* Creates a plugin running tests against the live source of locally linked packages, with no rebuild.
|
|
22
|
+
*
|
|
23
|
+
* Imports of workspace members and of sibling checkouts linked into `node_modules` resolve to their TypeScript source,
|
|
24
|
+
* so edits are picked up as they are saved. Packages installed from the registry keep resolving to their build.
|
|
25
|
+
*
|
|
26
|
+
* Packages are looked up as Node does, from the `node_modules` nearest to the importer up through its ancestors, so
|
|
27
|
+
* links hoisted to a workspace root are found from any member; imports with no importer start from the Vite root.
|
|
28
|
+
*
|
|
29
|
+
* The plugin replaces `resolve.conditions`, which breaks registry packages whose exports name a condition without
|
|
30
|
+
* publishing its target: Vite does not fall back to `default` when that target is missing from the installed files.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
*
|
|
34
|
+
* Linked packages map each exported subpath to its source under a custom condition, next to the build:
|
|
35
|
+
*
|
|
36
|
+
* ```json
|
|
37
|
+
* {
|
|
38
|
+
* "exports": {
|
|
39
|
+
* ".": { "source": "./src/index.ts", "default": "./dist/index.js" }
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* The consuming project registers the plugin with the same condition in its Vite or Vitest configuration:
|
|
45
|
+
*
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { symlinks } from "@metreeca/rack/vite";
|
|
48
|
+
* import { defineConfig } from "vitest/config";
|
|
49
|
+
*
|
|
50
|
+
* export default defineConfig({
|
|
51
|
+
* plugins: [symlinks({ conditions: "source" })]
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @param options The resolution settings
|
|
56
|
+
*
|
|
57
|
+
* @returns A plugin resolving linked imports to source and leaving all others to the default resolution
|
|
58
|
+
*/
|
|
59
|
+
export function symlinks({ conditions }) {
|
|
60
|
+
const $conditions = typeof conditions === "string" ? [conditions] : conditions;
|
|
61
|
+
const manifests = new Map(); // memoised by link real path
|
|
62
|
+
return {
|
|
63
|
+
name: "symlinks",
|
|
64
|
+
enforce: "pre",
|
|
65
|
+
resolveId(id, importer) {
|
|
66
|
+
const name = id.match(/^(@[^/]+\/)?[^/]+/)?.[0];
|
|
67
|
+
const base = importer && isAbsolute(importer) ? dirname(importer) : this.environment.config.root;
|
|
68
|
+
const link = name && lookup(name, base);
|
|
69
|
+
if (link) {
|
|
70
|
+
try {
|
|
71
|
+
const path = realpathSync(link);
|
|
72
|
+
const target = exports(manifest(path), id, { conditions: $conditions })?.[0];
|
|
73
|
+
return target === undefined ? null : resolve(path, target);
|
|
74
|
+
}
|
|
75
|
+
catch { // subpath not exported or excluded by a null target
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
function lookup(name, directory) {
|
|
85
|
+
const path = (createRequire(join(directory, sep)).resolve.paths(name) ?? [])
|
|
86
|
+
.map(modules => join(modules, name))
|
|
87
|
+
.find(candidate => lstatSync(candidate, { throwIfNoEntry: false }));
|
|
88
|
+
return path !== undefined && lstatSync(path).isSymbolicLink() ? path : undefined;
|
|
89
|
+
}
|
|
90
|
+
function manifest(path) {
|
|
91
|
+
const cached = manifests.get(path);
|
|
92
|
+
if (cached === undefined) {
|
|
93
|
+
const loaded = JSON.parse(readFileSync(join(path, "package.json"), "utf8"));
|
|
94
|
+
manifests.set(path, loaded);
|
|
95
|
+
return loaded;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return cached;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=symlinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.js","sourceRoot":"","sources":["../../src/vite/symlinks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metreeca/rack",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Everyday tooling for npm package development.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
7
7
|
"tooling",
|
|
@@ -25,8 +25,13 @@
|
|
|
25
25
|
"type": "module",
|
|
26
26
|
"sideEffects": false,
|
|
27
27
|
"exports": {
|
|
28
|
-
"
|
|
29
|
-
"./
|
|
28
|
+
"./vite": "./dist/vite/index.js",
|
|
29
|
+
"./tool": "./dist/tool/index.js"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"cp": "./dist/tool/cp.js",
|
|
33
|
+
"ln": "./dist/tool/ln.js",
|
|
34
|
+
"rm": "./dist/tool/rm.js"
|
|
30
35
|
},
|
|
31
36
|
"files": [
|
|
32
37
|
"README.md",
|
|
@@ -44,7 +49,11 @@
|
|
|
44
49
|
"check": "npx vitest --typecheck run",
|
|
45
50
|
"proof": "npx typedoc && (npx typedoc --watch & npx wds --root-dir docs --watch --open)"
|
|
46
51
|
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"resolve.exports": "^2.0.4"
|
|
54
|
+
},
|
|
47
55
|
"devDependencies": {
|
|
56
|
+
"@types/node": "^22.20.5",
|
|
48
57
|
"@web/dev-server": "^1.0.0",
|
|
49
58
|
"typedoc": "^0.28.14",
|
|
50
59
|
"typescript": "^5.9.3",
|
|
@@ -54,6 +63,11 @@
|
|
|
54
63
|
"peerDependencies": {
|
|
55
64
|
"vite": "^8.0.0"
|
|
56
65
|
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"vite": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
57
71
|
"allowScripts": {
|
|
58
72
|
"fsevents": false
|
|
59
73
|
}
|
package/dist/index.d.ts
DELETED
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC"}
|
package/dist/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © 2025-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
|
-
export {};
|
|
17
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|