@rebasepro/cli 0.18.1 → 0.19.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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Give compiled output the relative-import extensions Node's ESM loader needs.
3
+ *
4
+ * ## Why a project needs this at all
5
+ *
6
+ * TypeScript does not rewrite module specifiers, deliberately and by long-stated
7
+ * policy: what you write is what it emits. Node's ESM loader, equally
8
+ * deliberately, resolves no extensions — `./authors` is not a module, only
9
+ * `./authors.js` is. Between those two positions sits every project that writes
10
+ * extensionless relative imports, which is most of them, and which
11
+ * `moduleResolution: "bundler"` — what this scaffold ships — is designed for.
12
+ *
13
+ * The usual escape is to write the *output* extension in the *source*:
14
+ * `import x from "./authors.js"` in a file called `authors.ts`. It works, and it
15
+ * is a tax on every import in the project to satisfy a step the toolchain could
16
+ * take itself. This is that step.
17
+ *
18
+ * ## Why it is a command
19
+ *
20
+ * `rebase build` has always done this to its own bundle — the specifiers in
21
+ * `dist-bundle` are complete because {@link normalizeEsmSpecifiers} completes
22
+ * them. Nothing did it for the plain `tsc` output that `rebase eject` runs
23
+ * (`node dist/backend/src/index.js`) or that a self-hoster runs after
24
+ * `pnpm build`. So the templates wrote `.js` in source, which is the tax, and
25
+ * removing it silently broke eject at `pnpm start` while `check:eject` — which
26
+ * typechecks with `noEmit` — stayed green.
27
+ *
28
+ * One implementation, on both paths, callable from a package.json script.
29
+ *
30
+ * Mechanical and verifiable: only relative specifiers are touched, and only when
31
+ * the target resolves to a file that exists. Idempotent, so a build that runs it
32
+ * twice is a build that runs it once.
33
+ */
34
+ export declare const NORMALIZE_IMPORTS_FLAGS: {
35
+ /** Report what would change without writing anything. */
36
+ readonly "--check": BooleanConstructor;
37
+ };
38
+ export declare function normalizeImportsCommand(rawArgs: string[]): Promise<void>;
package/dist/index.es.js CHANGED
@@ -10838,6 +10838,98 @@ async function statusCommand$1(rawArgs) {
10838
10838
  console.log("");
10839
10839
  }
10840
10840
  //#endregion
10841
+ //#region src/commands/normalize-imports.ts
10842
+ /**
10843
+ * Give compiled output the relative-import extensions Node's ESM loader needs.
10844
+ *
10845
+ * ## Why a project needs this at all
10846
+ *
10847
+ * TypeScript does not rewrite module specifiers, deliberately and by long-stated
10848
+ * policy: what you write is what it emits. Node's ESM loader, equally
10849
+ * deliberately, resolves no extensions — `./authors` is not a module, only
10850
+ * `./authors.js` is. Between those two positions sits every project that writes
10851
+ * extensionless relative imports, which is most of them, and which
10852
+ * `moduleResolution: "bundler"` — what this scaffold ships — is designed for.
10853
+ *
10854
+ * The usual escape is to write the *output* extension in the *source*:
10855
+ * `import x from "./authors.js"` in a file called `authors.ts`. It works, and it
10856
+ * is a tax on every import in the project to satisfy a step the toolchain could
10857
+ * take itself. This is that step.
10858
+ *
10859
+ * ## Why it is a command
10860
+ *
10861
+ * `rebase build` has always done this to its own bundle — the specifiers in
10862
+ * `dist-bundle` are complete because {@link normalizeEsmSpecifiers} completes
10863
+ * them. Nothing did it for the plain `tsc` output that `rebase eject` runs
10864
+ * (`node dist/backend/src/index.js`) or that a self-hoster runs after
10865
+ * `pnpm build`. So the templates wrote `.js` in source, which is the tax, and
10866
+ * removing it silently broke eject at `pnpm start` while `check:eject` — which
10867
+ * typechecks with `noEmit` — stayed green.
10868
+ *
10869
+ * One implementation, on both paths, callable from a package.json script.
10870
+ *
10871
+ * Mechanical and verifiable: only relative specifiers are touched, and only when
10872
+ * the target resolves to a file that exists. Idempotent, so a build that runs it
10873
+ * twice is a build that runs it once.
10874
+ */
10875
+ var NORMALIZE_IMPORTS_FLAGS = {
10876
+ /** Report what would change without writing anything. */
10877
+ "--check": Boolean };
10878
+ var HELP = `
10879
+ ${chalk.bold("rebase normalize-imports")} — complete the relative imports in compiled output
10880
+
10881
+ ${chalk.green.bold("Usage")}
10882
+ rebase normalize-imports ${chalk.blue("<dir>")} [--check]
10883
+
10884
+ ${chalk.green.bold("What it does")}
10885
+ TypeScript emits your module specifiers verbatim, and Node's ESM loader does
10886
+ not guess extensions — so \`./authors\` compiles fine and fails at run time.
10887
+ This rewrites each relative specifier in the emitted JavaScript to the file it
10888
+ actually resolves to, so your source can stay extensionless.
10889
+
10890
+ Only relative specifiers, only when the target exists on disk, and idempotent.
10891
+
10892
+ ${chalk.green.bold("Arguments")}
10893
+ ${chalk.blue("<dir>")} The directory tsc emitted into, e.g. ${chalk.cyan("dist")}
10894
+
10895
+ ${chalk.green.bold("Options")}
10896
+ ${chalk.blue("--check")} Report what would change and exit 1 if anything would
10897
+ ${chalk.gray("(for CI — the build itself should just run the rewrite)")}
10898
+
10899
+ ${chalk.green.bold("Where it runs")}
10900
+ The scaffold's ${chalk.cyan("config")} and ${chalk.cyan("backend")} build scripts, after ${chalk.cyan("tsc")}.
10901
+ ${chalk.cyan("rebase build")} already does the same to the bundle it writes.
10902
+ `;
10903
+ async function normalizeImportsCommand(rawArgs) {
10904
+ const { flags, positionals, help } = parseCommandArgs({
10905
+ spec: NORMALIZE_IMPORTS_FLAGS,
10906
+ rawArgs,
10907
+ commandWords: 1,
10908
+ command: "normalize-imports",
10909
+ maxPositionals: 1
10910
+ });
10911
+ if (help) {
10912
+ console.log(HELP);
10913
+ return;
10914
+ }
10915
+ const target = positionals[0] ?? "dist";
10916
+ const outDir = path.resolve(process.cwd(), target);
10917
+ if (!fs.existsSync(outDir)) throw new Error(`${target} does not exist — run your build first. This step rewrites what the compiler emitted; it does not compile.`);
10918
+ const { rewritten, unresolved } = normalizeEsmSpecifiers(outDir);
10919
+ if (unresolved.length > 0) {
10920
+ console.log(chalk.yellow(` ⚠ ${unresolved.length} relative import(s) resolve to no file:`));
10921
+ for (const specifier of unresolved.slice(0, 10)) console.log(chalk.dim(` ${specifier}`));
10922
+ if (unresolved.length > 10) console.log(chalk.dim(` …and ${unresolved.length - 10} more`));
10923
+ console.log(chalk.dim(" These will throw ERR_MODULE_NOT_FOUND at run time."));
10924
+ }
10925
+ if (flags["--check"]) {
10926
+ if (rewritten > 0 || unresolved.length > 0) throw new Error(`${rewritten} import(s) are incomplete for Node ESM. Run \`rebase normalize-imports ${target}\` as part of the build.`);
10927
+ console.log(chalk.green(`✓ every relative import in ${target} is complete`));
10928
+ return;
10929
+ }
10930
+ console.log(rewritten > 0 ? chalk.dim(` completed ${rewritten} relative import(s) in ${target}/ for Node ESM`) : chalk.dim(` ${target}/ — every relative import was already complete`));
10931
+ }
10932
+ //#endregion
10841
10933
  //#region src/commands/skills.ts
10842
10934
  var require = createRequire(import.meta.url);
10843
10935
  /**
@@ -19014,6 +19106,9 @@ async function entry(args) {
19014
19106
  case "dev":
19015
19107
  await devCommand(args);
19016
19108
  break;
19109
+ case "normalize-imports":
19110
+ await normalizeImportsCommand(args);
19111
+ break;
19017
19112
  case "build":
19018
19113
  await buildCommand(args);
19019
19114
  break;
@@ -19065,6 +19160,7 @@ ${chalk.green.bold("Commands")}
19065
19160
  ${chalk.blue.bold("init")} Create a new Rebase project
19066
19161
  ${chalk.blue.bold("dev")} Start the development server
19067
19162
  ${chalk.blue.bold("build")} Build the apps declared in rebase.json into a bundle
19163
+ ${chalk.blue.bold("normalize-imports")} Complete compiled output's relative imports for Node ESM
19068
19164
  ${chalk.blue.bold("start")} Start the backend server ${chalk.gray("(production)")}
19069
19165
  ${chalk.blue.bold("apps list")} Show the apps this repository declares
19070
19166