ts-node-pack 0.1.3 → 0.2.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 CHANGED
@@ -25,18 +25,19 @@ Requires Node ≥ 20 and TypeScript ≥ 5.7 available in the package being packe
25
25
  ## Usage
26
26
 
27
27
  ```sh
28
- ts-node-pack <packageDir> [--tsconfig <path>] [--emit-only] [--keep-temp] [--verbose]
28
+ ts-node-pack <packageDir> [--tsconfig <path>] [--stage-to <dir>] [--skip-pack] [--force] [--verbose]
29
29
  ```
30
30
 
31
31
  | Flag | Description |
32
32
  | ------------------- | -------------------------------------------------------------------------------------------- |
33
33
  | `--tsconfig <path>` | tsconfig to extend. Defaults to `tsconfig.build.json` if present, otherwise `tsconfig.json`. |
34
- | `--emit-only` | Run emit + rewrites + validation, but skip `npm pack`. Prints the staging directory. |
35
- | `--keep-temp` | Do not delete the temporary staging directory on exit. |
34
+ | `--stage-to <dir>` | Stage into `<dir>` instead of an auto-created temp dir. Caller owns its lifecycle. |
35
+ | `--skip-pack` | Skip the final `npm pack` step. Requires `--stage-to`. |
36
+ | `--force` | With `--stage-to`, clear `<dir>` if it already has contents. |
36
37
  | `-v`, `--verbose` | Log each pipeline phase to stderr. |
37
38
  | `-h`, `--help` | Show help. |
38
39
 
39
- The resulting `<name>-<version>.tgz` is written to the current working directory.
40
+ The resulting `<name>-<version>.tgz` is written to the current working directory (unless `--skip-pack` is set).
40
41
 
41
42
  ### Example
42
43
 
@@ -46,20 +47,35 @@ ts-node-pack ./packages/core --verbose
46
47
  npm install ./my-core-1.2.3.tgz
47
48
  ```
48
49
 
50
+ ### `--stage-to` and `--skip-pack`
51
+
52
+ By default `ts-node-pack` stages into an auto-created `mkdtemp()` directory, runs `npm pack` against it, copies the resulting `.tgz` to the current working directory, and removes the temp dir.
53
+
54
+ Pass `--stage-to <dir>` when you want to keep the staged contents — for example, to let another tool pack from that directory instead (`lerna publish --contents <dir>`, an alternate tarball builder, etc.). `<dir>` must either not exist, be empty, or be opted-in for clearing via `--force`.
55
+
56
+ Combine with `--skip-pack` to stop after staging and never run `npm pack` at all. `--skip-pack` is only valid with `--stage-to` (otherwise the staged contents would have no accessible location).
57
+
58
+ | Invocation | Behavior |
59
+ | ------------------------------------------------- | ------------------------------------------------------------------- |
60
+ | `ts-node-pack <pkg>` | Stage to temp dir, pack, copy `.tgz` to CWD, delete temp dir. |
61
+ | `ts-node-pack <pkg> --stage-to <dir>` | Stage to `<dir>`, pack, copy `.tgz` to CWD, leave `<dir>` in place. |
62
+ | `ts-node-pack <pkg> --stage-to <dir> --skip-pack` | Stage to `<dir>`, skip pack, leave `<dir>` in place. |
63
+ | `ts-node-pack <pkg> --skip-pack` | Error: `skipPack requires stageTo`. |
64
+
49
65
  ## Pipeline
50
66
 
51
67
  1. **Resolve package** — read `package.json`, pick tsconfig.
52
- 2. **Stage** — create `mkdtemp()/package/`.
53
- 3. **Derived tsconfig** — write `tsconfig.emit.json` _inside the temp dir_ that `extends` the chosen tsconfig (by absolute path) and forces `outDir`, `declaration`, `rewriteRelativeImportExtensions: true`, `noEmit: false`. If the base tsconfig enables `sourceMap`, `inlineSourceMap`, or `declarationMap`, `inlineSources: true` is also set so debuggers get full source-level fidelity without any `.ts` files in the tarball.
68
+ 2. **Stage** — use `--stage-to <dir>` if given, otherwise create `mkdtemp()/package/`. A small separate `mkdtemp()` work dir always holds auxiliary files (e.g. the derived tsconfig) so they never land in the packed contents.
69
+ 3. **Derived tsconfig** — write `tsconfig.emit.json` _inside the work dir_ that `extends` the chosen tsconfig (by absolute path) and forces `outDir`, `declaration`, `rewriteRelativeImportExtensions: true`, `noEmit: false`. If the base tsconfig enables `sourceMap`, `inlineSourceMap`, or `declarationMap`, `inlineSources: true` is also set so debuggers get full source-level fidelity without any `.ts` files in the tarball.
54
70
  4. **Emit** — run `tsc -p` against the derived config.
55
71
  5. **Rewrite `.d.ts`** — for each emitted `.d.ts`, rewrite `./foo.ts` → `./foo.js` in `import` / `export from` / dynamic `import()` specifiers. Non-relative specifiers are left alone.
56
72
  6. **Rewrite `package.json`** — rewrite `.ts` → `.js` (and → `.d.ts` under `types` conditions) in `main`, `module`, `types`, `typings`, `bin`, `exports`, and the `files` array. Strip `devDependencies` and `scripts`.
57
73
  7. **Copy assets** — `README*`, `LICENSE*`, `CHANGELOG*`, `NOTICE*`. Source `.ts` files are never copied.
58
74
  8. **Validate** — fail if any `.ts` specifier remains in emitted `.js` / `.d.ts` / `package.json`, or if a referenced entry point does not exist.
59
- 9. **Pack** — `npm pack` in the staging directory; move the tarball to the original CWD.
60
- 10. **Cleanup** — remove `.ts-node-pack/` and the temp directory (unless `--keep-temp`).
75
+ 9. **Pack** — unless `--skip-pack`: `npm pack` in the staging directory and move the tarball to the original CWD.
76
+ 10. **Cleanup** — always remove the work dir. In default mode this also removes the staging dir (which is nested inside). In `--stage-to` mode the staging dir is the caller's, and survives.
61
77
 
62
- The source tree is never mutated. All intermediate artifacts (derived tsconfig, staging dir, tarball) live under a single `mkdtemp()` directory that is removed on exit.
78
+ The source tree is never mutated.
63
79
 
64
80
  ### Sourcemaps
65
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-node-pack",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Pack a TypeScript package into a Node-compatible npm tarball without modifying the source tree",
5
5
  "keywords": [
6
6
  "cli",
package/src/cli.js CHANGED
@@ -8,8 +8,9 @@ const { values, positionals } = parseArgs({
8
8
  allowPositionals: true,
9
9
  options: {
10
10
  tsconfig: { type: "string" },
11
- "emit-only": { type: "boolean", default: false },
12
- "keep-temp": { type: "boolean", default: false },
11
+ "skip-pack": { type: "boolean", default: false },
12
+ "stage-to": { type: "string" },
13
+ force: { type: "boolean", default: false },
13
14
  verbose: { type: "boolean", short: "v", default: false },
14
15
  help: { type: "boolean", short: "h", default: false },
15
16
  },
@@ -20,10 +21,12 @@ if (values.help || positionals.length === 0) {
20
21
 
21
22
  Options:
22
23
  --tsconfig <path> Path to tsconfig (default: tsconfig.build.json or tsconfig.json)
23
- --emit-only Emit compiled files without packing
24
- --keep-temp Keep temporary staging directory
25
- -v, --verbose Verbose output
26
- -h, --help Show this help message
24
+ --stage-to <dir> Stage into <dir> instead of an auto-created temp dir.
25
+ Caller owns cleanup. Errors if <dir> is non-empty unless --force.
26
+ --skip-pack Skip the final \`npm pack\` step. Requires --stage-to.
27
+ --force With --stage-to, clear <dir> if it already has contents.
28
+ -v, --verbose Log each pipeline phase to stderr.
29
+ -h, --help Show this help message.
27
30
  `);
28
31
  process.exit(values.help ? 0 : 1);
29
32
  }
@@ -33,8 +36,9 @@ const packageDir = resolve(positionals[0]);
33
36
  try {
34
37
  const result = await tsNodePack(packageDir, {
35
38
  tsconfig: values.tsconfig,
36
- emitOnly: values["emit-only"],
37
- keepTemp: values["keep-temp"],
39
+ skipPack: values["skip-pack"],
40
+ stageTo: values["stage-to"],
41
+ force: values.force,
38
42
  verbose: values.verbose,
39
43
  });
40
44
  console.log(result);
package/src/index.d.ts CHANGED
@@ -1,12 +1,24 @@
1
1
  export interface TsNodePackOptions {
2
2
  tsconfig?: string;
3
- emitOnly?: boolean;
4
- keepTemp?: boolean;
3
+ /**
4
+ * Skip the final `npm pack` step. Requires `stageTo` (otherwise there
5
+ * is no way for the caller to access the staged contents).
6
+ */
7
+ skipPack?: boolean;
8
+ /**
9
+ * Stage directly into this directory instead of an auto-created temp
10
+ * dir. Caller owns cleanup. Errors if the directory already has
11
+ * contents, unless `force` is set.
12
+ */
13
+ stageTo?: string;
14
+ /** With `stageTo`, clear the target directory if it already has contents. */
15
+ force?: boolean;
5
16
  verbose?: boolean;
6
17
  }
7
18
  /**
8
19
  * Main pipeline: pack a TypeScript package into a Node-compatible tarball.
9
- * Returns the path to the .tgz file (or the staging dir if --emit-only).
20
+ * Returns the path to the .tgz file, or the staging directory when
21
+ * `skipPack` is set.
10
22
  */
11
23
  export declare function tsNodePack(packageDir: string, options?: TsNodePackOptions): Promise<string>;
12
24
  /**
package/src/index.js CHANGED
@@ -21,22 +21,40 @@ const execFileAsync = promisify(execFile);
21
21
 
22
22
 
23
23
 
24
+
25
+
26
+
27
+
24
28
 
25
-
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
26
37
 
27
38
 
28
39
 
29
40
  /**
30
41
  * Main pipeline: pack a TypeScript package into a Node-compatible tarball.
31
- * Returns the path to the .tgz file (or the staging dir if --emit-only).
42
+ * Returns the path to the .tgz file, or the staging directory when
43
+ * `skipPack` is set.
32
44
  */
33
45
  export async function tsNodePack(
34
46
  packageDir ,
35
47
  options = {},
36
48
  ) {
37
- const { tsconfig, emitOnly, keepTemp, verbose } = options;
49
+ const { tsconfig, skipPack, stageTo, force, verbose } = options;
38
50
  const log = verbose ? (...args ) => console.error("[ts-node-pack]", ...args) : () => {};
39
51
 
52
+ if (skipPack && !stageTo) {
53
+ throw new Error(
54
+ "skipPack requires stageTo: caller must specify where to put the staged contents",
55
+ );
56
+ }
57
+
40
58
  packageDir = resolve(packageDir);
41
59
 
42
60
  // ── Phase 1: Resolve package ──────────────────────────────────────────
@@ -48,11 +66,34 @@ export async function tsNodePack(
48
66
  const tsconfigPath = resolveTsconfig(packageDir, tsconfig);
49
67
  if (tsconfigPath) log(`Found tsconfig: ${tsconfigPath}`);
50
68
 
51
- // ── Phase 2: Create staging directory ─────────────────────────────────
52
- log("Phase 2: Creating staging directory...");
53
- const tmpDir = await mkdtemp(join(tmpdir(), "ts-node-pack-"));
54
- const stagingDir = join(tmpDir, "package");
55
- await mkdir(stagingDir, { recursive: true });
69
+ // ── Phase 2: Create work and staging directories ─────────────────────
70
+ // We always need a private work dir for auxiliary files (e.g.
71
+ // tsconfig.emit.json) that MUST NOT land inside the packed contents.
72
+ // In default mode the staging dir is nested inside the work dir, so
73
+ // cleaning up the work dir cleans up everything. In stageTo mode the
74
+ // staging dir is the caller's directory — we still create a small
75
+ // separate work dir for auxiliary files and rm only that in Phase 10.
76
+ log("Phase 2: Creating work and staging directories...");
77
+ const workDir = await mkdtemp(join(tmpdir(), "ts-node-pack-"));
78
+ let stagingDir ;
79
+ if (stageTo) {
80
+ stagingDir = resolve(stageTo);
81
+ if (existsSync(stagingDir)) {
82
+ const entries = await readdir(stagingDir);
83
+ if (entries.length > 0) {
84
+ if (!force) {
85
+ throw new Error(
86
+ `stageTo directory is not empty: ${stagingDir}. Pass force: true to clear it.`,
87
+ );
88
+ }
89
+ await rm(stagingDir, { recursive: true, force: true });
90
+ }
91
+ }
92
+ await mkdir(stagingDir, { recursive: true });
93
+ } else {
94
+ stagingDir = join(workDir, "package");
95
+ await mkdir(stagingDir, { recursive: true });
96
+ }
56
97
  log(`Staging: ${stagingDir}`);
57
98
 
58
99
  try {
@@ -100,7 +141,7 @@ export async function tsNodePack(
100
141
 
101
142
  if (shouldRunTsc) {
102
143
  log("Phase 4: Generating derived tsconfig...");
103
- const emitConfigPath = join(tmpDir, "tsconfig.emit.json");
144
+ const emitConfigPath = join(workDir, "tsconfig.emit.json");
104
145
  // tsc's typeRoots walk-up starts at the config's directory, so it
105
146
  // can't reach packageDir/node_modules/@types from our temp-dir
106
147
  // location. Pin typeRoots when that directory exists. (TS 6.0
@@ -187,26 +228,31 @@ export async function tsNodePack(
187
228
  await validate(stagingDir, rewrittenPkg, log);
188
229
 
189
230
  // ── Phase 9: Pack ─────────────────────────────────────────────────────
190
- if (!emitOnly) {
231
+ if (!skipPack) {
191
232
  log("Phase 9: Packing...");
192
233
  const tgzPath = await pack(stagingDir);
193
234
  const tgzName = basename(tgzPath);
194
235
  const dest = join(process.cwd(), tgzName);
195
236
  await copyFile(tgzPath, dest);
237
+ // In stageTo mode the whole staging dir survives into the caller's
238
+ // filesystem, so the intermediate .tgz that `npm pack` wrote in
239
+ // there would stick around as visible clutter. Remove it. (In
240
+ // default mode the entire workDir is rm'd in Phase 10, so this
241
+ // unlink is redundant but harmless.)
242
+ if (stageTo) await unlink(tgzPath);
196
243
  log(`Created: ${dest}`);
197
244
  return dest;
198
245
  }
199
246
 
200
- log(`Emit-only mode. Staging directory: ${stagingDir}`);
247
+ log(`Skip-pack mode. Staging directory: ${stagingDir}`);
201
248
  return stagingDir;
202
249
  } finally {
203
250
  // ── Phase 10: Cleanup ───────────────────────────────────────────────
204
- if (!emitOnly && !keepTemp) {
205
- log("Phase 10: Cleaning up temp directory...");
206
- await rm(tmpDir, { recursive: true, force: true });
207
- } else if (keepTemp) {
208
- log(`Keeping temp directory: ${tmpDir}`);
209
- }
251
+ // Always rm the work dir. In default mode this also removes the
252
+ // staging dir (nested inside). In stageTo mode the staging dir is
253
+ // the caller's directory and survives.
254
+ log("Phase 10: Cleaning up work directory...");
255
+ await rm(workDir, { recursive: true, force: true });
210
256
  }
211
257
  }
212
258