tonnage 0.1.1 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # tonnage
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 9866ade: Upgrade comline, pass full runtime argv, add command and config-path completion with shell installation commands, and report ignored-option warnings on stderr while preserving asynchronous config loading.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 1ca268e: Link generated reports back to the tonnage repository.
14
+
3
15
  ## 0.1.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -31,12 +31,43 @@ Run `tonnage write` during development to update the README. Run
31
31
  nonzero when the README is stale. Run `tonnage --help` to see both commands and
32
32
  their optional config-path arguments.
33
33
 
34
+ Pass a config path with `tonnage check path/to/tonnage.config.ts` or
35
+ `tonnage write path/to/tonnage.config.ts`. Otherwise, Tonnage looks in the current
36
+ directory for `tonnage.config.ts`, then `tonnage.config.mjs`, then
37
+ `tonnage.config.js`. Config modules may use top-level `await`. Missing configs
38
+ are errors. Use `--` before a config path that starts with a dash.
39
+
40
+ Unknown options are ignored with warnings on stderr. Warnings do not change the
41
+ exit status; `check` still exits nonzero when the report is stale.
42
+
34
43
  By default the tool reads `package.json`, writes `README.md`, and measures every
35
44
  public export except `./package.json`. Paths are resolved relative to the config
36
45
  file. Use `exports.include` or `exports.exclude` to select subpaths, recipe entry
37
46
  files to describe realistic runtime boundaries, and `external` for non-peer
38
47
  imports that the package intentionally leaves to its consumers.
39
48
 
49
+ ### Shell completion
50
+
51
+ With `tonnage` available on your shell's `PATH`, install completion for your shell:
52
+
53
+ ```sh
54
+ tonnage completion install bash
55
+ tonnage completion install zsh
56
+ tonnage completion install fish
57
+ tonnage completion install nushell
58
+ tonnage completion install carapace
59
+ ```
60
+
61
+ Choose the integration you use. Completion suggests commands, help options, and
62
+ config paths without loading your config or measuring bundles. Installation
63
+ checks the shell's prerequisites and writes to its completion directory without
64
+ editing shell profiles. It reports any setup needed for your shell. If Nushell
65
+ already uses Carapace, install only the Carapace integration.
66
+
67
+ Run `tonnage completion bash` (or another target above) to print the integration
68
+ file without installing it. The `completion` command and comline's internal
69
+ completion protocol commands are reserved for shell integration.
70
+
40
71
  ## Report layout
41
72
 
42
73
  Generated reports adapt to the measurements they contain. An exports-only or
package/dist/cli.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { t as runTonnage } from "./run-P2rhqYM3.mjs";
2
+ import { t as runTonnage } from "./run-CZ_FO4C7.mjs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
- import { cli, help, helpOption, optional } from "comline";
6
+ import { cli, completionResponse, help, helpOption, logWarnings, optional } from "comline";
7
7
  //#region src/command-line.ts
8
8
  const ROOT_MANUAL = helpOption(`Show help for the tonnage command line.`);
9
9
  const WRITE_MANUAL = helpOption(`Update the configured README report.`);
@@ -22,20 +22,32 @@ const tonnageCli = cli({
22
22
  write: WRITE_MANUAL,
23
23
  "write/$configPath": WRITE_MANUAL
24
24
  },
25
+ positionalCompletions: {
26
+ "check/$configPath": { fileSystem: `files` },
27
+ "write/$configPath": { fileSystem: `files` }
28
+ },
25
29
  discoverConfigPath: () => void 0
26
30
  });
31
+ function completeTonnageCli(args) {
32
+ return completionResponse(tonnageCli.definition, args);
33
+ }
27
34
  function parseTonnageCli(args) {
28
- const { inputs } = tonnageCli(args);
29
- if (inputs.case === `` || inputs.opts.help) return { kind: `help` };
35
+ const { inputs, warnings } = tonnageCli(args);
36
+ if (inputs.case === `` || inputs.opts.help) return {
37
+ kind: `help`,
38
+ warnings
39
+ };
30
40
  const mode = inputs.case === `write` || inputs.case === `write/$configPath` ? `write` : `check`;
31
41
  const configArgument = inputs.path[1];
32
42
  return configArgument === void 0 ? {
33
43
  kind: `run`,
34
- mode
44
+ mode,
45
+ warnings
35
46
  } : {
36
47
  configArgument,
37
48
  kind: `run`,
38
- mode
49
+ mode,
50
+ warnings
39
51
  };
40
52
  }
41
53
  function renderTonnageCliHelp() {
@@ -49,7 +61,13 @@ const DEFAULT_CONFIG_FILES = [
49
61
  `tonnage.config.js`
50
62
  ];
51
63
  async function main() {
52
- const invocation = parseTonnageCli([`tonnage`, ...process.argv.slice(2)]);
64
+ const completion = await completeTonnageCli(process.argv);
65
+ if (completion !== void 0) {
66
+ process.stdout.write(completion);
67
+ return;
68
+ }
69
+ const invocation = parseTonnageCli(process.argv);
70
+ logWarnings(invocation.warnings);
53
71
  if (invocation.kind === `help`) {
54
72
  process.stdout.write(renderTonnageCliHelp());
55
73
  return;
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/command-line.ts","../src/cli.ts"],"sourcesContent":["import { cli, help, helpOption, optional } from \"comline\"\n\nimport type { TonnageMode } from \"./types.ts\"\n\nconst ROOT_MANUAL = helpOption(`Show help for the tonnage command line.`)\nconst WRITE_MANUAL = helpOption(`Update the configured README report.`)\nconst CHECK_MANUAL = helpOption(\n\t`Check that the configured README report is up to date.`,\n)\n\nconst tonnageCli = cli({\n\tcliName: `tonnage`,\n\tcliDescription: `Keep deterministic bundle size reports in README files and CI.`,\n\troutes: optional({\n\t\tcheck: optional({ $configPath: null }),\n\t\twrite: optional({ $configPath: null }),\n\t}),\n\trouteOptions: {\n\t\t\"\": ROOT_MANUAL,\n\t\tcheck: CHECK_MANUAL,\n\t\t\"check/$configPath\": CHECK_MANUAL,\n\t\twrite: WRITE_MANUAL,\n\t\t\"write/$configPath\": WRITE_MANUAL,\n\t},\n\tdiscoverConfigPath: () => undefined,\n})\n\nexport type TonnageCliInvocation =\n\t| { kind: `help` }\n\t| { configArgument?: string; kind: `run`; mode: TonnageMode }\n\nexport function parseTonnageCli(args: string[]): TonnageCliInvocation {\n\tconst { inputs } = tonnageCli(args)\n\n\tif (inputs.case === `` || inputs.opts.help) {\n\t\treturn { kind: `help` }\n\t}\n\n\tconst mode =\n\t\tinputs.case === `write` || inputs.case === `write/$configPath`\n\t\t\t? `write`\n\t\t\t: `check`\n\tconst configArgument = inputs.path[1]\n\n\treturn configArgument === undefined\n\t\t? { kind: `run`, mode }\n\t\t: { configArgument, kind: `run`, mode }\n}\n\nexport function renderTonnageCliHelp(): string {\n\treturn help(tonnageCli.definition)\n}\n","#!/usr/bin/env node\n\nimport fs from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport { parseTonnageCli, renderTonnageCliHelp } from \"./command-line.ts\"\nimport { runTonnage } from \"./run.ts\"\nimport type { TonnageConfig } from \"./types.ts\"\n\nconst DEFAULT_CONFIG_FILES = [\n\t`tonnage.config.ts`,\n\t`tonnage.config.mjs`,\n\t`tonnage.config.js`,\n]\n\nasync function main(): Promise<void> {\n\tconst invocation = parseTonnageCli([`tonnage`, ...process.argv.slice(2)])\n\tif (invocation.kind === `help`) {\n\t\tprocess.stdout.write(renderTonnageCliHelp())\n\t\treturn\n\t}\n\n\tconst { configArgument, mode } = invocation\n\tconst configPath = await findConfig(configArgument)\n\tconst config = await loadConfig(configPath)\n\tconst result = await runTonnage(config, {\n\t\tconfigDirectory: path.dirname(configPath),\n\t\tmode,\n\t})\n\n\tif (mode === `check` && result.changed) {\n\t\tprocess.stderr.write(\n\t\t\t`${path.relative(process.cwd(), result.readmePath)} is out of date. Run tonnage write.\\n`,\n\t\t)\n\t\tprocess.exitCode = 1\n\t\treturn\n\t}\n\n\tconst readme = path.relative(process.cwd(), result.readmePath)\n\tprocess.stdout.write(\n\t\tresult.changed\n\t\t\t? `Updated ${readme}.\\n`\n\t\t\t: `${readme} is already up to date.\\n`,\n\t)\n}\n\nasync function findConfig(configArgument: string | undefined): Promise<string> {\n\tif (configArgument) {\n\t\treturn path.resolve(configArgument)\n\t}\n\n\tfor (const filename of DEFAULT_CONFIG_FILES) {\n\t\tconst candidate = path.resolve(filename)\n\t\ttry {\n\t\t\tawait fs.access(candidate)\n\t\t\treturn candidate\n\t\t} catch {\n\t\t\t// Keep looking for the next supported config filename.\n\t\t}\n\t}\n\n\tthrow new Error(\n\t\t`Could not find a tonnage config (${DEFAULT_CONFIG_FILES.join(`, `)}).`,\n\t)\n}\n\nasync function loadConfig(configPath: string): Promise<TonnageConfig> {\n\tconst imported = (await import(pathToFileURL(configPath).href)) as {\n\t\tdefault?: unknown\n\t}\n\tif (!imported.default || typeof imported.default !== `object`) {\n\t\tthrow new Error(`${configPath} must default-export a tonnage config.`)\n\t}\n\treturn imported.default\n}\n\nmain().catch((error: unknown) => {\n\tprocess.stderr.write(\n\t\t`${error instanceof Error ? error.message : String(error)}\\n`,\n\t)\n\tprocess.exitCode = 1\n})\n"],"mappings":";;;;;;;AAIA,MAAM,cAAc,WAAW,yCAAyC;AACxE,MAAM,eAAe,WAAW,sCAAsC;AACtE,MAAM,eAAe,WACpB,wDACD;AAEA,MAAM,aAAa,IAAI;CACtB,SAAS;CACT,gBAAgB;CAChB,QAAQ,SAAS;EAChB,OAAO,SAAS,EAAE,aAAa,KAAK,CAAC;EACrC,OAAO,SAAS,EAAE,aAAa,KAAK,CAAC;CACtC,CAAC;CACD,cAAc;EACb,IAAI;EACJ,OAAO;EACP,qBAAqB;EACrB,OAAO;EACP,qBAAqB;CACtB;CACA,0BAA0B,KAAA;AAC3B,CAAC;AAMD,SAAgB,gBAAgB,MAAsC;CACrE,MAAM,EAAE,WAAW,WAAW,IAAI;CAElC,IAAI,OAAO,SAAS,MAAM,OAAO,KAAK,MACrC,OAAO,EAAE,MAAM,OAAO;CAGvB,MAAM,OACL,OAAO,SAAS,WAAW,OAAO,SAAS,sBACxC,UACA;CACJ,MAAM,iBAAiB,OAAO,KAAK;CAEnC,OAAO,mBAAmB,KAAA,IACvB;EAAE,MAAM;EAAO;CAAK,IACpB;EAAE;EAAgB,MAAM;EAAO;CAAK;AACxC;AAEA,SAAgB,uBAA+B;CAC9C,OAAO,KAAK,WAAW,UAAU;AAClC;;;ACzCA,MAAM,uBAAuB;CAC5B;CACA;CACA;AACD;AAEA,eAAe,OAAsB;CACpC,MAAM,aAAa,gBAAgB,CAAC,WAAW,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;CACxE,IAAI,WAAW,SAAS,QAAQ;EAC/B,QAAQ,OAAO,MAAM,qBAAqB,CAAC;EAC3C;CACD;CAEA,MAAM,EAAE,gBAAgB,SAAS;CACjC,MAAM,aAAa,MAAM,WAAW,cAAc;CAClD,MAAM,SAAS,MAAM,WAAW,UAAU;CAC1C,MAAM,SAAS,MAAM,WAAW,QAAQ;EACvC,iBAAiB,KAAK,QAAQ,UAAU;EACxC;CACD,CAAC;CAED,IAAI,SAAS,WAAW,OAAO,SAAS;EACvC,QAAQ,OAAO,MACd,GAAG,KAAK,SAAS,QAAQ,IAAI,GAAG,OAAO,UAAU,EAAE,sCACpD;EACA,QAAQ,WAAW;EACnB;CACD;CAEA,MAAM,SAAS,KAAK,SAAS,QAAQ,IAAI,GAAG,OAAO,UAAU;CAC7D,QAAQ,OAAO,MACd,OAAO,UACJ,WAAW,OAAO,OAClB,GAAG,OAAO,0BACd;AACD;AAEA,eAAe,WAAW,gBAAqD;CAC9E,IAAI,gBACH,OAAO,KAAK,QAAQ,cAAc;CAGnC,KAAK,MAAM,YAAY,sBAAsB;EAC5C,MAAM,YAAY,KAAK,QAAQ,QAAQ;EACvC,IAAI;GACH,MAAM,GAAG,OAAO,SAAS;GACzB,OAAO;EACR,QAAQ,CAER;CACD;CAEA,MAAM,IAAI,MACT,oCAAoC,qBAAqB,KAAK,IAAI,EAAE,GACrE;AACD;AAEA,eAAe,WAAW,YAA4C;CACrE,MAAM,WAAY,MAAM,OAAO,cAAc,UAAU,CAAC,CAAC;CAGzD,IAAI,CAAC,SAAS,WAAW,OAAO,SAAS,YAAY,UACpD,MAAM,IAAI,MAAM,GAAG,WAAW,uCAAuC;CAEtE,OAAO,SAAS;AACjB;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAChC,QAAQ,OAAO,MACd,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAC3D;CACA,QAAQ,WAAW;AACpB,CAAC"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/command-line.ts","../src/cli.ts"],"sourcesContent":["import {\n\ttype CliWarning,\n\tcli,\n\tcompletionResponse,\n\thelp,\n\thelpOption,\n\toptional,\n} from \"comline\"\n\nimport type { TonnageMode } from \"./types.ts\"\n\nconst ROOT_MANUAL = helpOption(`Show help for the tonnage command line.`)\nconst WRITE_MANUAL = helpOption(`Update the configured README report.`)\nconst CHECK_MANUAL = helpOption(\n\t`Check that the configured README report is up to date.`,\n)\n\nconst tonnageCli = cli({\n\tcliName: `tonnage`,\n\tcliDescription: `Keep deterministic bundle size reports in README files and CI.`,\n\troutes: optional({\n\t\tcheck: optional({ $configPath: null }),\n\t\twrite: optional({ $configPath: null }),\n\t}),\n\trouteOptions: {\n\t\t\"\": ROOT_MANUAL,\n\t\tcheck: CHECK_MANUAL,\n\t\t\"check/$configPath\": CHECK_MANUAL,\n\t\twrite: WRITE_MANUAL,\n\t\t\"write/$configPath\": WRITE_MANUAL,\n\t},\n\tpositionalCompletions: {\n\t\t\"check/$configPath\": { fileSystem: `files` },\n\t\t\"write/$configPath\": { fileSystem: `files` },\n\t},\n\tdiscoverConfigPath: () => undefined,\n})\n\nexport type TonnageCliInvocation = { warnings: CliWarning[] } & (\n\t| { kind: `help` }\n\t| { configArgument?: string; kind: `run`; mode: TonnageMode }\n)\n\nexport function completeTonnageCli(\n\targs: string[],\n): Promise<string | undefined> {\n\treturn completionResponse(tonnageCli.definition, args)\n}\n\nexport function parseTonnageCli(args: string[]): TonnageCliInvocation {\n\tconst { inputs, warnings } = tonnageCli(args)\n\n\tif (inputs.case === `` || inputs.opts.help) {\n\t\treturn { kind: `help`, warnings }\n\t}\n\n\tconst mode =\n\t\tinputs.case === `write` || inputs.case === `write/$configPath`\n\t\t\t? `write`\n\t\t\t: `check`\n\tconst configArgument = inputs.path[1]\n\n\treturn configArgument === undefined\n\t\t? { kind: `run`, mode, warnings }\n\t\t: { configArgument, kind: `run`, mode, warnings }\n}\n\nexport function renderTonnageCliHelp(): string {\n\treturn help(tonnageCli.definition)\n}\n","#!/usr/bin/env node\n\nimport fs from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport { logWarnings } from \"comline\"\n\nimport {\n\tcompleteTonnageCli,\n\tparseTonnageCli,\n\trenderTonnageCliHelp,\n} from \"./command-line.ts\"\nimport { runTonnage } from \"./run.ts\"\nimport type { TonnageConfig } from \"./types.ts\"\n\nconst DEFAULT_CONFIG_FILES = [\n\t`tonnage.config.ts`,\n\t`tonnage.config.mjs`,\n\t`tonnage.config.js`,\n]\n\nasync function main(): Promise<void> {\n\tconst completion = await completeTonnageCli(process.argv)\n\tif (completion !== undefined) {\n\t\tprocess.stdout.write(completion)\n\t\treturn\n\t}\n\n\tconst invocation = parseTonnageCli(process.argv)\n\tlogWarnings(invocation.warnings)\n\tif (invocation.kind === `help`) {\n\t\tprocess.stdout.write(renderTonnageCliHelp())\n\t\treturn\n\t}\n\n\tconst { configArgument, mode } = invocation\n\tconst configPath = await findConfig(configArgument)\n\tconst config = await loadConfig(configPath)\n\tconst result = await runTonnage(config, {\n\t\tconfigDirectory: path.dirname(configPath),\n\t\tmode,\n\t})\n\n\tif (mode === `check` && result.changed) {\n\t\tprocess.stderr.write(\n\t\t\t`${path.relative(process.cwd(), result.readmePath)} is out of date. Run tonnage write.\\n`,\n\t\t)\n\t\tprocess.exitCode = 1\n\t\treturn\n\t}\n\n\tconst readme = path.relative(process.cwd(), result.readmePath)\n\tprocess.stdout.write(\n\t\tresult.changed\n\t\t\t? `Updated ${readme}.\\n`\n\t\t\t: `${readme} is already up to date.\\n`,\n\t)\n}\n\nasync function findConfig(configArgument: string | undefined): Promise<string> {\n\tif (configArgument) {\n\t\treturn path.resolve(configArgument)\n\t}\n\n\tfor (const filename of DEFAULT_CONFIG_FILES) {\n\t\tconst candidate = path.resolve(filename)\n\t\ttry {\n\t\t\tawait fs.access(candidate)\n\t\t\treturn candidate\n\t\t} catch {\n\t\t\t// Keep looking for the next supported config filename.\n\t\t}\n\t}\n\n\tthrow new Error(\n\t\t`Could not find a tonnage config (${DEFAULT_CONFIG_FILES.join(`, `)}).`,\n\t)\n}\n\nasync function loadConfig(configPath: string): Promise<TonnageConfig> {\n\tconst imported = (await import(pathToFileURL(configPath).href)) as {\n\t\tdefault?: unknown\n\t}\n\tif (!imported.default || typeof imported.default !== `object`) {\n\t\tthrow new Error(`${configPath} must default-export a tonnage config.`)\n\t}\n\treturn imported.default\n}\n\nmain().catch((error: unknown) => {\n\tprocess.stderr.write(\n\t\t`${error instanceof Error ? error.message : String(error)}\\n`,\n\t)\n\tprocess.exitCode = 1\n})\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -47,7 +47,7 @@ type TonnageRunResult = {
47
47
  };
48
48
  //#endregion
49
49
  //#region src/define-config.d.ts
50
- declare function defineConfig(config: TonnageConfig): TonnageConfig;
50
+ export declare function defineConfig(config: TonnageConfig): TonnageConfig;
51
51
  //#endregion
52
52
  //#region src/measure.d.ts
53
53
  type MeasureImportsOptions = {
@@ -57,25 +57,25 @@ type MeasureImportsOptions = {
57
57
  target?: string | undefined;
58
58
  };
59
59
  type MeasureEntryOptions = MeasureImportsOptions;
60
- declare function measureEntry(entry: string, options: MeasureEntryOptions): Promise<BundleMeasurement>;
61
- declare function measureImports(imports: readonly string[], options: MeasureImportsOptions): Promise<BundleMeasurement>;
60
+ export declare function measureEntry(entry: string, options: MeasureEntryOptions): Promise<BundleMeasurement>;
61
+ export declare function measureImports(imports: readonly string[], options: MeasureImportsOptions): Promise<BundleMeasurement>;
62
62
  //#endregion
63
63
  //#region src/readme.d.ts
64
- declare function tonnageMarkers(marker?: string): {
64
+ export declare function tonnageMarkers(marker?: string): {
65
65
  end: string;
66
66
  start: string;
67
67
  };
68
- declare function updateGeneratedSection(readme: string, markdown: string, marker?: string): string;
68
+ export declare function updateGeneratedSection(readme: string, markdown: string, marker?: string): string;
69
69
  //#endregion
70
70
  //#region src/report.d.ts
71
- declare function createTonnageReport(config: TonnageConfig, configDirectory: string): Promise<TonnageReport>;
72
- declare function renderTonnageMarkdown(report: TonnageReport, config?: TonnageConfig): string;
71
+ export declare function createTonnageReport(config: TonnageConfig, configDirectory: string): Promise<TonnageReport>;
72
+ export declare function renderTonnageMarkdown(report: TonnageReport, config?: TonnageConfig): string;
73
73
  //#endregion
74
74
  //#region src/run.d.ts
75
- declare function runTonnage(config: TonnageConfig, options: {
75
+ export declare function runTonnage(config: TonnageConfig, options: {
76
76
  configDirectory?: string;
77
77
  mode: TonnageMode;
78
78
  }): Promise<TonnageRunResult>;
79
79
  //#endregion
80
- export { type BundleMeasurement, type BundlePlatform, type MeasureEntryOptions, type MeasureImportsOptions, type TonnageConfig, type TonnageExports, type TonnageMode, type TonnageRecipe, type TonnageRecipeRow, type TonnageReport, type TonnageRow, type TonnageRunResult, createTonnageReport, defineConfig, measureEntry, measureImports, renderTonnageMarkdown, runTonnage, tonnageMarkers, updateGeneratedSection };
80
+ export type { BundleMeasurement, BundlePlatform, MeasureEntryOptions, MeasureImportsOptions, TonnageConfig, TonnageExports, TonnageMode, TonnageRecipe, TonnageRecipeRow, TonnageReport, TonnageRow, TonnageRunResult };
81
81
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/define-config.ts","../src/measure.ts","../src/readme.ts","../src/report.ts","../src/run.ts"],"mappings":";;KAAY;KAEA;EACX;EACA;;KAGW;EACX;EACA;EACA;EACA,WAAW;;KAGA;EACX,oBAAoB;EACpB;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,mBAAmB;EACnB;;KAGW;EACX;EACA;;KAGW,aAAa;EACxB;EACA;;KAGW,mBAAmB;EAC9B;EACA;;KAGW;EACX,kBAAkB;EAClB;EACA,kBAAkB;;KAGP;KAEA;EACX;EACA;EACA,QAAQ;;;;iBClDO,aAAa,QAAQ,gBAAgB;;;KCKzC;EACX;EACA,WAAW;EACX;EACA;;KAGW,sBAAsB;iBAMZ,aACrB,eACA,SAAS,sBACP,QAAQ;iBAcW,eACrB,4BACA,SAAS,wBACP,QAAQ;;;iBCxCK,eAAe;EAC9B;EACA;;iBASe,uBACf,gBACA,kBACA;;;iBCOqB,oBACrB,QAAQ,eACR,0BACE,QAAQ;iBA+DK,sBACf,QAAQ,eACR,SAAQ;;;iBClFa,WACrB,QAAQ,eACR;EACC;EACA,MAAM;IAEL,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/define-config.ts","../src/measure.ts","../src/readme.ts","../src/report.ts","../src/run.ts"],"mappings":";;KAAY;KAEA;EACX;EACA;;KAGW;EACX;EACA;EACA;EACA,WAAW;;KAGA;EACX,oBAAoB;EACpB;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,mBAAmB;EACnB;;KAGW;EACX;EACA;;KAGW,aAAa;EACxB;EACA;;KAGW,mBAAmB;EAC9B;EACA;;KAGW;EACX,kBAAkB;EAClB;EACA,kBAAkB;;KAGP;KAEA;EACX;EACA;EACA,QAAQ;;;;wBClDO,aAAa,QAAQ,gBAAgB;;;KCKzC;EACX;EACA,WAAW;EACX;EACA;;KAGW,sBAAsB;wBAMZ,aACrB,eACA,SAAS,sBACP,QAAQ;wBAcW,eACrB,4BACA,SAAS,wBACP,QAAQ;;;wBCxCK,eAAe;EAC9B;EACA;;wBASe,uBACf,gBACA,kBACA;;;wBCOqB,oBACrB,QAAQ,eACR,0BACE,QAAQ;wBA+DK,sBACf,QAAQ,eACR,SAAQ;;;wBClFa,WACrB,QAAQ,eACR;EACC;EACA,MAAM;IAEL,QAAQ"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as measureImports, i as measureEntry, n as createTonnageReport, o as tonnageMarkers, r as renderTonnageMarkdown, s as updateGeneratedSection, t as runTonnage } from "./run-P2rhqYM3.mjs";
1
+ import { a as measureImports, i as measureEntry, n as createTonnageReport, o as tonnageMarkers, r as renderTonnageMarkdown, s as updateGeneratedSection, t as runTonnage } from "./run-CZ_FO4C7.mjs";
2
2
  //#region src/define-config.ts
3
3
  function defineConfig(config) {
4
4
  return config;
@@ -188,6 +188,7 @@ function renderTonnageMarkdown(report, config = {}) {
188
188
  `right`,
189
189
  `right`
190
190
  ]));
191
+ lines.push(``, `Report maintained with [tonnage](https://github.com/jeremybanka/tonnage).`);
191
192
  return lines.join(`\n`);
192
193
  }
193
194
  function methodologyLines({ hasExports, hasRecipes }) {
@@ -260,4 +261,4 @@ async function runTonnage(config, options) {
260
261
  //#endregion
261
262
  export { measureImports as a, measureEntry as i, createTonnageReport as n, tonnageMarkers as o, renderTonnageMarkdown as r, updateGeneratedSection as s, runTonnage as t };
262
263
 
263
- //# sourceMappingURL=run-P2rhqYM3.mjs.map
264
+ //# sourceMappingURL=run-CZ_FO4C7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-CZ_FO4C7.mjs","names":[],"sources":["../src/readme.ts","../src/measure.ts","../src/package-resolution.ts","../src/report.ts","../src/run.ts"],"sourcesContent":["export function tonnageMarkers(marker = `default`): {\n\tend: string\n\tstart: string\n} {\n\tvalidateMarker(marker)\n\treturn {\n\t\tend: `<!-- tonnage:${marker}:end -->`,\n\t\tstart: `<!-- tonnage:${marker}:start -->`,\n\t}\n}\n\nexport function updateGeneratedSection(\n\treadme: string,\n\tmarkdown: string,\n\tmarker?: string,\n): string {\n\tconst { end, start } = tonnageMarkers(marker)\n\tconst startIndex = readme.indexOf(start)\n\tconst endIndex = readme.indexOf(end)\n\n\tif (startIndex === -1 || endIndex === -1) {\n\t\tthrow new Error(\n\t\t\t`README must contain exactly one ${start} and one ${end} marker.`,\n\t\t)\n\t}\n\tif (\n\t\treadme.slice(startIndex + start.length).includes(start) ||\n\t\treadme.slice(endIndex + end.length).includes(end)\n\t) {\n\t\tthrow new Error(\n\t\t\t`README must contain exactly one ${start} and one ${end} marker.`,\n\t\t)\n\t}\n\tif (endIndex < startIndex) {\n\t\tthrow new Error(`${end} must appear after ${start}.`)\n\t}\n\n\tconst contentStart = startIndex + start.length\n\treturn `${readme.slice(0, contentStart)}\\n\\n${markdown.trim()}\\n\\n${readme.slice(endIndex)}`\n}\n\nfunction validateMarker(marker: string): void {\n\tif (!/^[a-z0-9][a-z0-9._-]*$/i.test(marker)) {\n\t\tthrow new Error(\n\t\t\t`Tonnage marker names may contain letters, numbers, dots, underscores, and hyphens.`,\n\t\t)\n\t}\n}\n","import path from \"node:path\"\nimport { gzipSync } from \"node:zlib\"\n\nimport { build, type BuildOptions, type Plugin } from \"esbuild\"\n\nimport type { BundleMeasurement, BundlePlatform } from \"./types.ts\"\n\nexport type MeasureImportsOptions = {\n\texternal?: readonly string[] | undefined\n\tplatform?: BundlePlatform | undefined\n\tresolveDirectory: string\n\ttarget?: string | undefined\n}\n\nexport type MeasureEntryOptions = MeasureImportsOptions\n\ntype InternalMeasureOptions = MeasureImportsOptions & {\n\tplugins?: readonly Plugin[]\n}\n\nexport async function measureEntry(\n\tentry: string,\n\toptions: MeasureEntryOptions,\n): Promise<BundleMeasurement> {\n\treturn measureEntryWithPlugins(entry, options)\n}\n\nexport async function measureEntryWithPlugins(\n\tentry: string,\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\treturn measureBuild(\n\t\t{ entryPoints: [path.resolve(options.resolveDirectory, entry)] },\n\t\toptions,\n\t)\n}\n\nexport async function measureImports(\n\timports: readonly string[],\n\toptions: MeasureImportsOptions,\n): Promise<BundleMeasurement> {\n\treturn measureImportsWithPlugins(imports, options)\n}\n\nexport async function measureImportsWithPlugins(\n\timports: readonly string[],\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\tif (imports.length === 0) {\n\t\tthrow new Error(`A tonnage entry must import at least one module.`)\n\t}\n\n\tconst entry = imports\n\t\t.map(\n\t\t\t(specifier, index) =>\n\t\t\t\t`export * as entry${index} from ${JSON.stringify(specifier)}`,\n\t\t)\n\t\t.join(`\\n`)\n\treturn measureBuild(\n\t\t{\n\t\t\tstdin: {\n\t\t\t\tcontents: entry,\n\t\t\t\tloader: `js`,\n\t\t\t\tresolveDir: options.resolveDirectory,\n\t\t\t\tsourcefile: `tonnage-entry.js`,\n\t\t\t},\n\t\t},\n\t\toptions,\n\t)\n}\n\nasync function measureBuild(\n\tentry: Pick<BuildOptions, `entryPoints` | `stdin`>,\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\tconst platform = options.platform ?? `neutral`\n\n\tconst result = await build({\n\t\tbundle: true,\n\t\tcharset: `utf8`,\n\t\texternal: [\n\t\t\t...(options.external ?? []),\n\t\t\t...(platform === `browser` ? [] : [`node:*`]),\n\t\t],\n\t\tformat: `esm`,\n\t\tlegalComments: `none`,\n\t\tlogLevel: `silent`,\n\t\t...(platform === `neutral` ? { mainFields: [`module`, `main`] } : {}),\n\t\tminify: true,\n\t\toutdir: `out`,\n\t\tplatform,\n\t\tplugins: [...(options.plugins ?? [])],\n\t\t...entry,\n\t\ttarget: options.target ?? `es2022`,\n\t\ttreeShaking: true,\n\t\twrite: false,\n\t})\n\n\tconst outputFiles = [...(result.outputFiles ?? [])]\n\t\t.filter((output) => output.path.endsWith(`.js`))\n\t\t.sort((left, right) => left.path.localeCompare(right.path))\n\tif (outputFiles.length === 0) {\n\t\tthrow new Error(`The tonnage entry did not emit any runtime JavaScript.`)\n\t}\n\tconst rawBytes = outputFiles.reduce(\n\t\t(total, output) => total + output.contents.byteLength,\n\t\t0,\n\t)\n\tconst gzipBytes = outputFiles.reduce(\n\t\t(total, output) =>\n\t\t\ttotal + gzipSync(output.contents, { level: 9 }).byteLength,\n\t\t0,\n\t)\n\n\treturn { gzipBytes, rawBytes }\n}\n","import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nimport type { Plugin } from \"esbuild\"\n\nexport async function withPackageResolver<Result>(\n\tpackageName: string,\n\tpackageDirectory: string,\n\tuseResolver: (plugin: Plugin) => Promise<Result>,\n): Promise<Result> {\n\tconst temporaryDirectory = await fs.mkdtemp(\n\t\tpath.join(os.tmpdir(), `tonnage-package-`),\n\t)\n\ttry {\n\t\tconst nodeModulesDirectory = path.join(temporaryDirectory, `node_modules`)\n\t\tconst packageLinkPath = path.resolve(nodeModulesDirectory, packageName)\n\t\tif (!packageLinkPath.startsWith(`${nodeModulesDirectory}${path.sep}`)) {\n\t\t\tthrow new Error(`Invalid package name: ${packageName}`)\n\t\t}\n\n\t\tawait fs.mkdir(path.dirname(packageLinkPath), { recursive: true })\n\t\tawait fs.symlink(\n\t\t\tpackageDirectory,\n\t\t\tpackageLinkPath,\n\t\t\tprocess.platform === `win32` ? `junction` : `dir`,\n\t\t)\n\n\t\tconst filter = new RegExp(`^${escapeRegExp(packageName)}(?:/.*)?$`)\n\t\tconst plugin: Plugin = {\n\t\t\tname: `tonnage-package-resolution`,\n\t\t\tsetup(build) {\n\t\t\t\tbuild.onResolve({ filter }, async (args) => {\n\t\t\t\t\tif (args.resolveDir === temporaryDirectory) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\treturn build.resolve(args.path, {\n\t\t\t\t\t\timporter: args.importer,\n\t\t\t\t\t\tkind: args.kind,\n\t\t\t\t\t\tnamespace: args.namespace,\n\t\t\t\t\t\tresolveDir: temporaryDirectory,\n\t\t\t\t\t\twith: args.with,\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t}\n\n\t\treturn await useResolver(plugin)\n\t} finally {\n\t\tawait fs.rm(temporaryDirectory, { force: true, recursive: true })\n\t}\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport {\n\tmeasureEntryWithPlugins,\n\tmeasureImportsWithPlugins,\n} from \"./measure.ts\"\nimport { withPackageResolver } from \"./package-resolution.ts\"\nimport type {\n\tTonnageConfig,\n\tTonnageExports,\n\tTonnageReport,\n\tTonnageRow,\n} from \"./types.ts\"\n\ntype PackageJson = {\n\texports?: unknown\n\tname?: string\n\tpeerDependencies?: Record<string, string>\n}\n\nexport async function createTonnageReport(\n\tconfig: TonnageConfig,\n\tconfigDirectory: string,\n): Promise<TonnageReport> {\n\tconst packageJsonPath = path.resolve(\n\t\tconfigDirectory,\n\t\tconfig.packageJson ?? `package.json`,\n\t)\n\tconst packageJson = JSON.parse(\n\t\tawait fs.readFile(packageJsonPath, `utf8`),\n\t) as PackageJson\n\tif (!packageJson.name) {\n\t\tthrow new Error(`${packageJsonPath} does not declare a package name.`)\n\t}\n\n\tconst resolveDirectory = path.dirname(packageJsonPath)\n\tconst peerDependencies = Object.keys(packageJson.peerDependencies ?? {})\n\tconst external = unique([...peerDependencies, ...(config.external ?? [])])\n\tconst exportImports = selectExportImports(\n\t\tpackageJson.name,\n\t\tpackageJson.exports,\n\t\tconfig.exports,\n\t)\n\n\tconst { exportRows, recipeRows } = await withPackageResolver(\n\t\tpackageJson.name,\n\t\tresolveDirectory,\n\t\tasync (packageResolver) => {\n\t\t\tconst exportRows = await Promise.all(\n\t\t\t\texportImports.map(async (specifier): Promise<TonnageRow> => ({\n\t\t\t\t\t...(await measureImportsWithPlugins([specifier], {\n\t\t\t\t\t\texternal,\n\t\t\t\t\t\tplatform: config.platform,\n\t\t\t\t\t\tplugins: [packageResolver],\n\t\t\t\t\t\tresolveDirectory,\n\t\t\t\t\t\ttarget: config.target,\n\t\t\t\t\t})),\n\t\t\t\t\timports: [specifier],\n\t\t\t\t\tname: specifier,\n\t\t\t\t})),\n\t\t\t)\n\t\t\tconst recipeRows = await Promise.all(\n\t\t\t\t(config.recipes ?? []).map(async (recipe) => ({\n\t\t\t\t\t...(await measureEntryWithPlugins(recipe.entry, {\n\t\t\t\t\t\texternal: unique([...external, ...(recipe.external ?? [])]),\n\t\t\t\t\t\tplatform: recipe.platform ?? config.platform,\n\t\t\t\t\t\tplugins: [packageResolver],\n\t\t\t\t\t\tresolveDirectory,\n\t\t\t\t\t\ttarget: config.target,\n\t\t\t\t\t})),\n\t\t\t\t\tentry: recipe.entry,\n\t\t\t\t\tname: recipe.name,\n\t\t\t\t})),\n\t\t\t)\n\n\t\t\treturn { exportRows, recipeRows }\n\t\t},\n\t)\n\n\treturn {\n\t\texports: exportRows,\n\t\tpackageName: packageJson.name,\n\t\trecipes: recipeRows,\n\t}\n}\n\nexport function renderTonnageMarkdown(\n\treport: TonnageReport,\n\tconfig: TonnageConfig = {},\n): string {\n\tconst hasExports = report.exports.length > 0\n\tconst hasRecipes = report.recipes.length > 0\n\tconst hasMultipleSections = hasExports && hasRecipes\n\tconst lines = [\n\t\t`## ${config.heading ?? `Bundle size`}`,\n\t\t``,\n\t\t...methodologyLines({ hasExports, hasRecipes }),\n\t]\n\n\tif (hasExports) {\n\t\tlines.push(\n\t\t\t``,\n\t\t\t...(hasMultipleSections ? [`### Package exports`, ``] : []),\n\t\t\t...markdownTable(\n\t\t\t\t[`Import`, `Minified JS`, `Gzip JS`],\n\t\t\t\treport.exports.map((row) => [\n\t\t\t\t\tcode(row.name),\n\t\t\t\t\tformatBytes(row.rawBytes),\n\t\t\t\t\tformatBytes(row.gzipBytes),\n\t\t\t\t]),\n\t\t\t\t[`left`, `right`, `right`],\n\t\t\t),\n\t\t)\n\t}\n\n\tif (hasRecipes) {\n\t\tlines.push(\n\t\t\t``,\n\t\t\t...(hasMultipleSections ? [`### Usage examples`, ``] : []),\n\t\t\t...markdownTable(\n\t\t\t\t[`Recipe`, `Entry`, `Minified JS`, `Gzip JS`],\n\t\t\t\treport.recipes.map((row) => [\n\t\t\t\t\tescapeCell(row.name),\n\t\t\t\t\tcode(row.entry),\n\t\t\t\t\tformatBytes(row.rawBytes),\n\t\t\t\t\tformatBytes(row.gzipBytes),\n\t\t\t\t]),\n\t\t\t\t[`left`, `left`, `right`, `right`],\n\t\t\t),\n\t\t)\n\t}\n\n\tlines.push(\n\t\t``,\n\t\t`Report maintained with [tonnage](https://github.com/jeremybanka/tonnage).`,\n\t)\n\n\treturn lines.join(`\\n`)\n}\n\nfunction methodologyLines({\n\thasExports,\n\thasRecipes,\n}: {\n\thasExports: boolean\n\thasRecipes: boolean\n}): string[] {\n\tconst lines: string[] = []\n\tif (hasExports) {\n\t\tlines.push(`Package export sizes include complete runtime export surfaces.`)\n\t}\n\tif (hasRecipes) {\n\t\tlines.push(\n\t\t\t`Usage example sizes bundle their entry files and tree-shake unused exports.`,\n\t\t)\n\t}\n\tlines.push(\n\t\t`Sizes are exact minified and level-9 gzip JavaScript byte counts. Declarations, source maps, CSS, and other assets are excluded. Peer dependencies stay external, and shared modules are counted once per bundle.`,\n\t)\n\treturn lines\n}\n\nfunction selectExportImports(\n\tpackageName: string,\n\texportsValue: unknown,\n\tselection: TonnageConfig[`exports`],\n): string[] {\n\tif (selection === false) {\n\t\treturn []\n\t}\n\n\tconst exportKeys = getExportKeys(exportsValue)\n\tconst options: TonnageExports = typeof selection === `object` ? selection : {}\n\tconst included = options.include\n\t\t? [...options.include]\n\t\t: exportKeys.filter((exportKey) => exportKey !== `./package.json`)\n\tconst excluded = new Set(options.exclude ?? [])\n\n\treturn included\n\t\t.filter((exportKey) => !excluded.has(exportKey))\n\t\t.map((exportKey) => toPackageImport(packageName, exportKey))\n}\n\nfunction getExportKeys(exportsValue: unknown): string[] {\n\tif (typeof exportsValue === `string` || Array.isArray(exportsValue)) {\n\t\treturn [`.`]\n\t}\n\tif (!exportsValue || typeof exportsValue !== `object`) {\n\t\treturn [`.`]\n\t}\n\n\tconst keys = Object.keys(exportsValue)\n\treturn keys.some((key) => key.startsWith(`.`)) ? keys : [`.`]\n}\n\nfunction toPackageImport(packageName: string, exportKey: string): string {\n\tif (exportKey === `.`) {\n\t\treturn packageName\n\t}\n\tif (!exportKey.startsWith(`./`)) {\n\t\tthrow new Error(`Invalid package export key: ${exportKey}`)\n\t}\n\treturn `${packageName}/${exportKey.slice(2)}`\n}\n\nfunction formatBytes(bytes: number): string {\n\treturn `${new Intl.NumberFormat(`en-US`).format(bytes)} B`\n}\n\nfunction code(value: string): string {\n\treturn `<code>${escapeCell(value)}</code>`\n}\n\nfunction escapeCell(value: string): string {\n\treturn value.replaceAll(`&`, `&amp;`).replaceAll(`|`, `&#124;`)\n}\n\nfunction unique(values: readonly string[]): string[] {\n\treturn [...new Set(values)]\n}\n\nfunction markdownTable(\n\theadings: readonly string[],\n\trows: readonly (readonly string[])[],\n\talignments: readonly (`left` | `right`)[],\n): string[] {\n\tconst widths = headings.map((heading, column) =>\n\t\tMath.max(heading.length, ...rows.map((row) => row[column]?.length ?? 0), 3),\n\t)\n\tconst renderRow = (cells: readonly string[]): string =>\n\t\t`| ${cells.map((cell, column) => padCell(cell, widths[column] ?? 3, alignments[column] ?? `left`)).join(` | `)} |`\n\tconst separator = widths.map((width, column) =>\n\t\talignments[column] === `right`\n\t\t\t? `${`-`.repeat(width - 1)}:`\n\t\t\t: `-`.repeat(width),\n\t)\n\n\treturn [renderRow(headings), renderRow(separator), ...rows.map(renderRow)]\n}\n\nfunction padCell(\n\tcell: string,\n\twidth: number,\n\talignment: `left` | `right`,\n): string {\n\treturn alignment === `right` ? cell.padStart(width) : cell.padEnd(width)\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport { updateGeneratedSection } from \"./readme.ts\"\nimport { createTonnageReport, renderTonnageMarkdown } from \"./report.ts\"\nimport type { TonnageConfig, TonnageMode, TonnageRunResult } from \"./types.ts\"\n\nexport async function runTonnage(\n\tconfig: TonnageConfig,\n\toptions: {\n\t\tconfigDirectory?: string\n\t\tmode: TonnageMode\n\t},\n): Promise<TonnageRunResult> {\n\tconst configDirectory = path.resolve(options.configDirectory ?? process.cwd())\n\tconst readmePath = path.resolve(configDirectory, config.readme ?? `README.md`)\n\tconst oldReadme = await fs.readFile(readmePath, `utf8`)\n\tconst report = await createTonnageReport(config, configDirectory)\n\tconst markdown = renderTonnageMarkdown(report, config)\n\tconst newReadme = updateGeneratedSection(oldReadme, markdown, config.marker)\n\tconst changed = oldReadme !== newReadme\n\n\tif (options.mode === `write` && changed) {\n\t\tawait fs.writeFile(readmePath, newReadme)\n\t}\n\n\treturn { changed, readmePath, report }\n}\n"],"mappings":";;;;;;AAAA,SAAgB,eAAe,SAAS,WAGtC;CACD,eAAe,MAAM;CACrB,OAAO;EACN,KAAK,gBAAgB,OAAO;EAC5B,OAAO,gBAAgB,OAAO;CAC/B;AACD;AAEA,SAAgB,uBACf,QACA,UACA,QACS;CACT,MAAM,EAAE,KAAK,UAAU,eAAe,MAAM;CAC5C,MAAM,aAAa,OAAO,QAAQ,KAAK;CACvC,MAAM,WAAW,OAAO,QAAQ,GAAG;CAEnC,IAAI,eAAe,MAAM,aAAa,IACrC,MAAM,IAAI,MACT,mCAAmC,MAAM,WAAW,IAAI,SACzD;CAED,IACC,OAAO,MAAM,aAAa,MAAM,MAAM,CAAC,CAAC,SAAS,KAAK,KACtD,OAAO,MAAM,WAAW,IAAI,MAAM,CAAC,CAAC,SAAS,GAAG,GAEhD,MAAM,IAAI,MACT,mCAAmC,MAAM,WAAW,IAAI,SACzD;CAED,IAAI,WAAW,YACd,MAAM,IAAI,MAAM,GAAG,IAAI,qBAAqB,MAAM,EAAE;CAGrD,MAAM,eAAe,aAAa,MAAM;CACxC,OAAO,GAAG,OAAO,MAAM,GAAG,YAAY,EAAE,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,MAAM,QAAQ;AAC1F;AAEA,SAAS,eAAe,QAAsB;CAC7C,IAAI,CAAC,0BAA0B,KAAK,MAAM,GACzC,MAAM,IAAI,MACT,oFACD;AAEF;;;AC3BA,eAAsB,aACrB,OACA,SAC6B;CAC7B,OAAO,wBAAwB,OAAO,OAAO;AAC9C;AAEA,eAAsB,wBACrB,OACA,SAC6B;CAC7B,OAAO,aACN,EAAE,aAAa,CAAC,KAAK,QAAQ,QAAQ,kBAAkB,KAAK,CAAC,EAAE,GAC/D,OACD;AACD;AAEA,eAAsB,eACrB,SACA,SAC6B;CAC7B,OAAO,0BAA0B,SAAS,OAAO;AAClD;AAEA,eAAsB,0BACrB,SACA,SAC6B;CAC7B,IAAI,QAAQ,WAAW,GACtB,MAAM,IAAI,MAAM,kDAAkD;CASnE,OAAO,aACN,EACC,OAAO;EACN,UATW,QACZ,KACC,WAAW,UACX,oBAAoB,MAAM,QAAQ,KAAK,UAAU,SAAS,GAC5D,CAAC,CACA,KAAK,IAIU;EACd,QAAQ;EACR,YAAY,QAAQ;EACpB,YAAY;CACb,EACD,GACA,OACD;AACD;AAEA,eAAe,aACd,OACA,SAC6B;CAC7B,MAAM,WAAW,QAAQ,YAAY;CAuBrC,MAAM,cAAc,CAAC,IAAI,MArBJ,MAAM;EAC1B,QAAQ;EACR,SAAS;EACT,UAAU,CACT,GAAI,QAAQ,YAAY,CAAC,GACzB,GAAI,aAAa,YAAY,CAAC,IAAI,CAAC,QAAQ,CAC5C;EACA,QAAQ;EACR,eAAe;EACf,UAAU;EACV,GAAI,aAAa,YAAY,EAAE,YAAY,CAAC,UAAU,MAAM,EAAE,IAAI,CAAC;EACnE,QAAQ;EACR,QAAQ;EACR;EACA,SAAS,CAAC,GAAI,QAAQ,WAAW,CAAC,CAAE;EACpC,GAAG;EACH,QAAQ,QAAQ,UAAU;EAC1B,aAAa;EACb,OAAO;CACR,CAAC,EAAA,CAE+B,eAAe,CAAC,CAAE,CAAC,CACjD,QAAQ,WAAW,OAAO,KAAK,SAAS,KAAK,CAAC,CAAC,CAC/C,MAAM,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;CAC3D,IAAI,YAAY,WAAW,GAC1B,MAAM,IAAI,MAAM,wDAAwD;CAEzE,MAAM,WAAW,YAAY,QAC3B,OAAO,WAAW,QAAQ,OAAO,SAAS,YAC3C,CACD;CAOA,OAAO;EAAE,WANS,YAAY,QAC5B,OAAO,WACP,QAAQ,SAAS,OAAO,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,YACjD,CAGgB;EAAG;CAAS;AAC9B;;;AC7GA,eAAsB,oBACrB,aACA,kBACA,aACkB;CAClB,MAAM,qBAAqB,MAAM,GAAG,QACnC,KAAK,KAAK,GAAG,OAAO,GAAG,kBAAkB,CAC1C;CACA,IAAI;EACH,MAAM,uBAAuB,KAAK,KAAK,oBAAoB,cAAc;EACzE,MAAM,kBAAkB,KAAK,QAAQ,sBAAsB,WAAW;EACtE,IAAI,CAAC,gBAAgB,WAAW,GAAG,uBAAuB,KAAK,KAAK,GACnE,MAAM,IAAI,MAAM,yBAAyB,aAAa;EAGvD,MAAM,GAAG,MAAM,KAAK,QAAQ,eAAe,GAAG,EAAE,WAAW,KAAK,CAAC;EACjE,MAAM,GAAG,QACR,kBACA,iBACA,QAAQ,aAAa,UAAU,aAAa,KAC7C;EAEA,MAAM,SAAS,IAAI,OAAO,IAAI,aAAa,WAAW,EAAE,UAAU;EAoBlE,OAAO,MAAM,YAAY;GAlBxB,MAAM;GACN,MAAM,OAAO;IACZ,MAAM,UAAU,EAAE,OAAO,GAAG,OAAO,SAAS;KAC3C,IAAI,KAAK,eAAe,oBACvB;KAGD,OAAO,MAAM,QAAQ,KAAK,MAAM;MAC/B,UAAU,KAAK;MACf,MAAM,KAAK;MACX,WAAW,KAAK;MAChB,YAAY;MACZ,MAAM,KAAK;KACZ,CAAC;IACF,CAAC;GACF;EAG6B,CAAC;CAChC,UAAU;EACT,MAAM,GAAG,GAAG,oBAAoB;GAAE,OAAO;GAAM,WAAW;EAAK,CAAC;CACjE;AACD;AAEA,SAAS,aAAa,OAAuB;CAC5C,OAAO,MAAM,QAAQ,uBAAuB,MAAM;AACnD;;;ACnCA,eAAsB,oBACrB,QACA,iBACyB;CACzB,MAAM,kBAAkB,KAAK,QAC5B,iBACA,OAAO,eAAe,cACvB;CACA,MAAM,cAAc,KAAK,MACxB,MAAM,GAAG,SAAS,iBAAiB,MAAM,CAC1C;CACA,IAAI,CAAC,YAAY,MAChB,MAAM,IAAI,MAAM,GAAG,gBAAgB,kCAAkC;CAGtE,MAAM,mBAAmB,KAAK,QAAQ,eAAe;CAErD,MAAM,WAAW,OAAO,CAAC,GADA,OAAO,KAAK,YAAY,oBAAoB,CAAC,CAC3B,GAAG,GAAI,OAAO,YAAY,CAAC,CAAE,CAAC;CACzE,MAAM,gBAAgB,oBACrB,YAAY,MACZ,YAAY,SACZ,OAAO,OACR;CAEA,MAAM,EAAE,YAAY,eAAe,MAAM,oBACxC,YAAY,MACZ,kBACA,OAAO,oBAAoB;EA4B1B,OAAO;GAAE,YAAA,MA3BgB,QAAQ,IAChC,cAAc,IAAI,OAAO,eAAoC;IAC5D,GAAI,MAAM,0BAA0B,CAAC,SAAS,GAAG;KAChD;KACA,UAAU,OAAO;KACjB,SAAS,CAAC,eAAe;KACzB;KACA,QAAQ,OAAO;IAChB,CAAC;IACD,SAAS,CAAC,SAAS;IACnB,MAAM;GACP,EAAE,CACH;GAeqB,YAAA,MAdI,QAAQ,KAC/B,OAAO,WAAW,CAAC,EAAA,CAAG,IAAI,OAAO,YAAY;IAC7C,GAAI,MAAM,wBAAwB,OAAO,OAAO;KAC/C,UAAU,OAAO,CAAC,GAAG,UAAU,GAAI,OAAO,YAAY,CAAC,CAAE,CAAC;KAC1D,UAAU,OAAO,YAAY,OAAO;KACpC,SAAS,CAAC,eAAe;KACzB;KACA,QAAQ,OAAO;IAChB,CAAC;IACD,OAAO,OAAO;IACd,MAAM,OAAO;GACd,EAAE,CACH;EAEgC;CACjC,CACD;CAEA,OAAO;EACN,SAAS;EACT,aAAa,YAAY;EACzB,SAAS;CACV;AACD;AAEA,SAAgB,sBACf,QACA,SAAwB,CAAC,GAChB;CACT,MAAM,aAAa,OAAO,QAAQ,SAAS;CAC3C,MAAM,aAAa,OAAO,QAAQ,SAAS;CAC3C,MAAM,sBAAsB,cAAc;CAC1C,MAAM,QAAQ;EACb,MAAM,OAAO,WAAW;EACxB;EACA,GAAG,iBAAiB;GAAE;GAAY;EAAW,CAAC;CAC/C;CAEA,IAAI,YACH,MAAM,KACL,IACA,GAAI,sBAAsB,CAAC,uBAAuB,EAAE,IAAI,CAAC,GACzD,GAAG,cACF;EAAC;EAAU;EAAe;CAAS,GACnC,OAAO,QAAQ,KAAK,QAAQ;EAC3B,KAAK,IAAI,IAAI;EACb,YAAY,IAAI,QAAQ;EACxB,YAAY,IAAI,SAAS;CAC1B,CAAC,GACD;EAAC;EAAQ;EAAS;CAAO,CAC1B,CACD;CAGD,IAAI,YACH,MAAM,KACL,IACA,GAAI,sBAAsB,CAAC,sBAAsB,EAAE,IAAI,CAAC,GACxD,GAAG,cACF;EAAC;EAAU;EAAS;EAAe;CAAS,GAC5C,OAAO,QAAQ,KAAK,QAAQ;EAC3B,WAAW,IAAI,IAAI;EACnB,KAAK,IAAI,KAAK;EACd,YAAY,IAAI,QAAQ;EACxB,YAAY,IAAI,SAAS;CAC1B,CAAC,GACD;EAAC;EAAQ;EAAQ;EAAS;CAAO,CAClC,CACD;CAGD,MAAM,KACL,IACA,2EACD;CAEA,OAAO,MAAM,KAAK,IAAI;AACvB;AAEA,SAAS,iBAAiB,EACzB,YACA,cAIY;CACZ,MAAM,QAAkB,CAAC;CACzB,IAAI,YACH,MAAM,KAAK,gEAAgE;CAE5E,IAAI,YACH,MAAM,KACL,6EACD;CAED,MAAM,KACL,mNACD;CACA,OAAO;AACR;AAEA,SAAS,oBACR,aACA,cACA,WACW;CACX,IAAI,cAAc,OACjB,OAAO,CAAC;CAGT,MAAM,aAAa,cAAc,YAAY;CAC7C,MAAM,UAA0B,OAAO,cAAc,WAAW,YAAY,CAAC;CAC7E,MAAM,WAAW,QAAQ,UACtB,CAAC,GAAG,QAAQ,OAAO,IACnB,WAAW,QAAQ,cAAc,cAAc,gBAAgB;CAClE,MAAM,WAAW,IAAI,IAAI,QAAQ,WAAW,CAAC,CAAC;CAE9C,OAAO,SACL,QAAQ,cAAc,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAC/C,KAAK,cAAc,gBAAgB,aAAa,SAAS,CAAC;AAC7D;AAEA,SAAS,cAAc,cAAiC;CACvD,IAAI,OAAO,iBAAiB,YAAY,MAAM,QAAQ,YAAY,GACjE,OAAO,CAAC,GAAG;CAEZ,IAAI,CAAC,gBAAgB,OAAO,iBAAiB,UAC5C,OAAO,CAAC,GAAG;CAGZ,MAAM,OAAO,OAAO,KAAK,YAAY;CACrC,OAAO,KAAK,MAAM,QAAQ,IAAI,WAAW,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG;AAC7D;AAEA,SAAS,gBAAgB,aAAqB,WAA2B;CACxE,IAAI,cAAc,KACjB,OAAO;CAER,IAAI,CAAC,UAAU,WAAW,IAAI,GAC7B,MAAM,IAAI,MAAM,+BAA+B,WAAW;CAE3D,OAAO,GAAG,YAAY,GAAG,UAAU,MAAM,CAAC;AAC3C;AAEA,SAAS,YAAY,OAAuB;CAC3C,OAAO,GAAG,IAAI,KAAK,aAAa,OAAO,CAAC,CAAC,OAAO,KAAK,EAAE;AACxD;AAEA,SAAS,KAAK,OAAuB;CACpC,OAAO,SAAS,WAAW,KAAK,EAAE;AACnC;AAEA,SAAS,WAAW,OAAuB;CAC1C,OAAO,MAAM,WAAW,KAAK,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ;AAC/D;AAEA,SAAS,OAAO,QAAqC;CACpD,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;AAC3B;AAEA,SAAS,cACR,UACA,MACA,YACW;CACX,MAAM,SAAS,SAAS,KAAK,SAAS,WACrC,KAAK,IAAI,QAAQ,QAAQ,GAAG,KAAK,KAAK,QAAQ,IAAI,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC,CAC3E;CACA,MAAM,aAAa,UAClB,KAAK,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM,OAAO,WAAW,GAAG,WAAW,WAAW,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE;CAChH,MAAM,YAAY,OAAO,KAAK,OAAO,WACpC,WAAW,YAAY,UACpB,GAAG,IAAI,OAAO,QAAQ,CAAC,EAAE,KACzB,IAAI,OAAO,KAAK,CACpB;CAEA,OAAO;EAAC,UAAU,QAAQ;EAAG,UAAU,SAAS;EAAG,GAAG,KAAK,IAAI,SAAS;CAAC;AAC1E;AAEA,SAAS,QACR,MACA,OACA,WACS;CACT,OAAO,cAAc,UAAU,KAAK,SAAS,KAAK,IAAI,KAAK,OAAO,KAAK;AACxE;;;AChPA,eAAsB,WACrB,QACA,SAI4B;CAC5B,MAAM,kBAAkB,KAAK,QAAQ,QAAQ,mBAAmB,QAAQ,IAAI,CAAC;CAC7E,MAAM,aAAa,KAAK,QAAQ,iBAAiB,OAAO,UAAU,WAAW;CAC7E,MAAM,YAAY,MAAM,GAAG,SAAS,YAAY,MAAM;CACtD,MAAM,SAAS,MAAM,oBAAoB,QAAQ,eAAe;CAEhE,MAAM,YAAY,uBAAuB,WADxB,sBAAsB,QAAQ,MACK,GAAU,OAAO,MAAM;CAC3E,MAAM,UAAU,cAAc;CAE9B,IAAI,QAAQ,SAAS,WAAW,SAC/B,MAAM,GAAG,UAAU,YAAY,SAAS;CAGzC,OAAO;EAAE;EAAS;EAAY;CAAO;AACtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tonnage",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Keep deterministic bundle size reports in README files and CI.",
5
5
  "type": "module",
6
6
  "license": "MPL-2.0",
@@ -37,11 +37,11 @@
37
37
  "main": "./dist/index.mjs",
38
38
  "types": "./dist/index.d.mts",
39
39
  "dependencies": {
40
- "comline": "0.5.2",
40
+ "comline": "0.7.0",
41
41
  "esbuild": "0.28.2"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/node": "26.4.0",
44
+ "@types/node": "26.5.1",
45
45
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24"
46
46
  },
47
47
  "scripts": {
package/src/cli.ts CHANGED
@@ -4,7 +4,13 @@ import fs from "node:fs/promises"
4
4
  import path from "node:path"
5
5
  import { pathToFileURL } from "node:url"
6
6
 
7
- import { parseTonnageCli, renderTonnageCliHelp } from "./command-line.ts"
7
+ import { logWarnings } from "comline"
8
+
9
+ import {
10
+ completeTonnageCli,
11
+ parseTonnageCli,
12
+ renderTonnageCliHelp,
13
+ } from "./command-line.ts"
8
14
  import { runTonnage } from "./run.ts"
9
15
  import type { TonnageConfig } from "./types.ts"
10
16
 
@@ -15,7 +21,14 @@ const DEFAULT_CONFIG_FILES = [
15
21
  ]
16
22
 
17
23
  async function main(): Promise<void> {
18
- const invocation = parseTonnageCli([`tonnage`, ...process.argv.slice(2)])
24
+ const completion = await completeTonnageCli(process.argv)
25
+ if (completion !== undefined) {
26
+ process.stdout.write(completion)
27
+ return
28
+ }
29
+
30
+ const invocation = parseTonnageCli(process.argv)
31
+ logWarnings(invocation.warnings)
19
32
  if (invocation.kind === `help`) {
20
33
  process.stdout.write(renderTonnageCliHelp())
21
34
  return
@@ -1,4 +1,11 @@
1
- import { cli, help, helpOption, optional } from "comline"
1
+ import {
2
+ type CliWarning,
3
+ cli,
4
+ completionResponse,
5
+ help,
6
+ helpOption,
7
+ optional,
8
+ } from "comline"
2
9
 
3
10
  import type { TonnageMode } from "./types.ts"
4
11
 
@@ -22,18 +29,29 @@ const tonnageCli = cli({
22
29
  write: WRITE_MANUAL,
23
30
  "write/$configPath": WRITE_MANUAL,
24
31
  },
32
+ positionalCompletions: {
33
+ "check/$configPath": { fileSystem: `files` },
34
+ "write/$configPath": { fileSystem: `files` },
35
+ },
25
36
  discoverConfigPath: () => undefined,
26
37
  })
27
38
 
28
- export type TonnageCliInvocation =
39
+ export type TonnageCliInvocation = { warnings: CliWarning[] } & (
29
40
  | { kind: `help` }
30
41
  | { configArgument?: string; kind: `run`; mode: TonnageMode }
42
+ )
43
+
44
+ export function completeTonnageCli(
45
+ args: string[],
46
+ ): Promise<string | undefined> {
47
+ return completionResponse(tonnageCli.definition, args)
48
+ }
31
49
 
32
50
  export function parseTonnageCli(args: string[]): TonnageCliInvocation {
33
- const { inputs } = tonnageCli(args)
51
+ const { inputs, warnings } = tonnageCli(args)
34
52
 
35
53
  if (inputs.case === `` || inputs.opts.help) {
36
- return { kind: `help` }
54
+ return { kind: `help`, warnings }
37
55
  }
38
56
 
39
57
  const mode =
@@ -43,8 +61,8 @@ export function parseTonnageCli(args: string[]): TonnageCliInvocation {
43
61
  const configArgument = inputs.path[1]
44
62
 
45
63
  return configArgument === undefined
46
- ? { kind: `run`, mode }
47
- : { configArgument, kind: `run`, mode }
64
+ ? { kind: `run`, mode, warnings }
65
+ : { configArgument, kind: `run`, mode, warnings }
48
66
  }
49
67
 
50
68
  export function renderTonnageCliHelp(): string {
package/src/report.ts CHANGED
@@ -131,6 +131,11 @@ export function renderTonnageMarkdown(
131
131
  )
132
132
  }
133
133
 
134
+ lines.push(
135
+ ``,
136
+ `Report maintained with [tonnage](https://github.com/jeremybanka/tonnage).`,
137
+ )
138
+
134
139
  return lines.join(`\n`)
135
140
  }
136
141
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-P2rhqYM3.mjs","names":[],"sources":["../src/readme.ts","../src/measure.ts","../src/package-resolution.ts","../src/report.ts","../src/run.ts"],"sourcesContent":["export function tonnageMarkers(marker = `default`): {\n\tend: string\n\tstart: string\n} {\n\tvalidateMarker(marker)\n\treturn {\n\t\tend: `<!-- tonnage:${marker}:end -->`,\n\t\tstart: `<!-- tonnage:${marker}:start -->`,\n\t}\n}\n\nexport function updateGeneratedSection(\n\treadme: string,\n\tmarkdown: string,\n\tmarker?: string,\n): string {\n\tconst { end, start } = tonnageMarkers(marker)\n\tconst startIndex = readme.indexOf(start)\n\tconst endIndex = readme.indexOf(end)\n\n\tif (startIndex === -1 || endIndex === -1) {\n\t\tthrow new Error(\n\t\t\t`README must contain exactly one ${start} and one ${end} marker.`,\n\t\t)\n\t}\n\tif (\n\t\treadme.slice(startIndex + start.length).includes(start) ||\n\t\treadme.slice(endIndex + end.length).includes(end)\n\t) {\n\t\tthrow new Error(\n\t\t\t`README must contain exactly one ${start} and one ${end} marker.`,\n\t\t)\n\t}\n\tif (endIndex < startIndex) {\n\t\tthrow new Error(`${end} must appear after ${start}.`)\n\t}\n\n\tconst contentStart = startIndex + start.length\n\treturn `${readme.slice(0, contentStart)}\\n\\n${markdown.trim()}\\n\\n${readme.slice(endIndex)}`\n}\n\nfunction validateMarker(marker: string): void {\n\tif (!/^[a-z0-9][a-z0-9._-]*$/i.test(marker)) {\n\t\tthrow new Error(\n\t\t\t`Tonnage marker names may contain letters, numbers, dots, underscores, and hyphens.`,\n\t\t)\n\t}\n}\n","import path from \"node:path\"\nimport { gzipSync } from \"node:zlib\"\n\nimport { build, type BuildOptions, type Plugin } from \"esbuild\"\n\nimport type { BundleMeasurement, BundlePlatform } from \"./types.ts\"\n\nexport type MeasureImportsOptions = {\n\texternal?: readonly string[] | undefined\n\tplatform?: BundlePlatform | undefined\n\tresolveDirectory: string\n\ttarget?: string | undefined\n}\n\nexport type MeasureEntryOptions = MeasureImportsOptions\n\ntype InternalMeasureOptions = MeasureImportsOptions & {\n\tplugins?: readonly Plugin[]\n}\n\nexport async function measureEntry(\n\tentry: string,\n\toptions: MeasureEntryOptions,\n): Promise<BundleMeasurement> {\n\treturn measureEntryWithPlugins(entry, options)\n}\n\nexport async function measureEntryWithPlugins(\n\tentry: string,\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\treturn measureBuild(\n\t\t{ entryPoints: [path.resolve(options.resolveDirectory, entry)] },\n\t\toptions,\n\t)\n}\n\nexport async function measureImports(\n\timports: readonly string[],\n\toptions: MeasureImportsOptions,\n): Promise<BundleMeasurement> {\n\treturn measureImportsWithPlugins(imports, options)\n}\n\nexport async function measureImportsWithPlugins(\n\timports: readonly string[],\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\tif (imports.length === 0) {\n\t\tthrow new Error(`A tonnage entry must import at least one module.`)\n\t}\n\n\tconst entry = imports\n\t\t.map(\n\t\t\t(specifier, index) =>\n\t\t\t\t`export * as entry${index} from ${JSON.stringify(specifier)}`,\n\t\t)\n\t\t.join(`\\n`)\n\treturn measureBuild(\n\t\t{\n\t\t\tstdin: {\n\t\t\t\tcontents: entry,\n\t\t\t\tloader: `js`,\n\t\t\t\tresolveDir: options.resolveDirectory,\n\t\t\t\tsourcefile: `tonnage-entry.js`,\n\t\t\t},\n\t\t},\n\t\toptions,\n\t)\n}\n\nasync function measureBuild(\n\tentry: Pick<BuildOptions, `entryPoints` | `stdin`>,\n\toptions: InternalMeasureOptions,\n): Promise<BundleMeasurement> {\n\tconst platform = options.platform ?? `neutral`\n\n\tconst result = await build({\n\t\tbundle: true,\n\t\tcharset: `utf8`,\n\t\texternal: [\n\t\t\t...(options.external ?? []),\n\t\t\t...(platform === `browser` ? [] : [`node:*`]),\n\t\t],\n\t\tformat: `esm`,\n\t\tlegalComments: `none`,\n\t\tlogLevel: `silent`,\n\t\t...(platform === `neutral` ? { mainFields: [`module`, `main`] } : {}),\n\t\tminify: true,\n\t\toutdir: `out`,\n\t\tplatform,\n\t\tplugins: [...(options.plugins ?? [])],\n\t\t...entry,\n\t\ttarget: options.target ?? `es2022`,\n\t\ttreeShaking: true,\n\t\twrite: false,\n\t})\n\n\tconst outputFiles = [...(result.outputFiles ?? [])]\n\t\t.filter((output) => output.path.endsWith(`.js`))\n\t\t.sort((left, right) => left.path.localeCompare(right.path))\n\tif (outputFiles.length === 0) {\n\t\tthrow new Error(`The tonnage entry did not emit any runtime JavaScript.`)\n\t}\n\tconst rawBytes = outputFiles.reduce(\n\t\t(total, output) => total + output.contents.byteLength,\n\t\t0,\n\t)\n\tconst gzipBytes = outputFiles.reduce(\n\t\t(total, output) =>\n\t\t\ttotal + gzipSync(output.contents, { level: 9 }).byteLength,\n\t\t0,\n\t)\n\n\treturn { gzipBytes, rawBytes }\n}\n","import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nimport type { Plugin } from \"esbuild\"\n\nexport async function withPackageResolver<Result>(\n\tpackageName: string,\n\tpackageDirectory: string,\n\tuseResolver: (plugin: Plugin) => Promise<Result>,\n): Promise<Result> {\n\tconst temporaryDirectory = await fs.mkdtemp(\n\t\tpath.join(os.tmpdir(), `tonnage-package-`),\n\t)\n\ttry {\n\t\tconst nodeModulesDirectory = path.join(temporaryDirectory, `node_modules`)\n\t\tconst packageLinkPath = path.resolve(nodeModulesDirectory, packageName)\n\t\tif (!packageLinkPath.startsWith(`${nodeModulesDirectory}${path.sep}`)) {\n\t\t\tthrow new Error(`Invalid package name: ${packageName}`)\n\t\t}\n\n\t\tawait fs.mkdir(path.dirname(packageLinkPath), { recursive: true })\n\t\tawait fs.symlink(\n\t\t\tpackageDirectory,\n\t\t\tpackageLinkPath,\n\t\t\tprocess.platform === `win32` ? `junction` : `dir`,\n\t\t)\n\n\t\tconst filter = new RegExp(`^${escapeRegExp(packageName)}(?:/.*)?$`)\n\t\tconst plugin: Plugin = {\n\t\t\tname: `tonnage-package-resolution`,\n\t\t\tsetup(build) {\n\t\t\t\tbuild.onResolve({ filter }, async (args) => {\n\t\t\t\t\tif (args.resolveDir === temporaryDirectory) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\treturn build.resolve(args.path, {\n\t\t\t\t\t\timporter: args.importer,\n\t\t\t\t\t\tkind: args.kind,\n\t\t\t\t\t\tnamespace: args.namespace,\n\t\t\t\t\t\tresolveDir: temporaryDirectory,\n\t\t\t\t\t\twith: args.with,\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t}\n\n\t\treturn await useResolver(plugin)\n\t} finally {\n\t\tawait fs.rm(temporaryDirectory, { force: true, recursive: true })\n\t}\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport {\n\tmeasureEntryWithPlugins,\n\tmeasureImportsWithPlugins,\n} from \"./measure.ts\"\nimport { withPackageResolver } from \"./package-resolution.ts\"\nimport type {\n\tTonnageConfig,\n\tTonnageExports,\n\tTonnageReport,\n\tTonnageRow,\n} from \"./types.ts\"\n\ntype PackageJson = {\n\texports?: unknown\n\tname?: string\n\tpeerDependencies?: Record<string, string>\n}\n\nexport async function createTonnageReport(\n\tconfig: TonnageConfig,\n\tconfigDirectory: string,\n): Promise<TonnageReport> {\n\tconst packageJsonPath = path.resolve(\n\t\tconfigDirectory,\n\t\tconfig.packageJson ?? `package.json`,\n\t)\n\tconst packageJson = JSON.parse(\n\t\tawait fs.readFile(packageJsonPath, `utf8`),\n\t) as PackageJson\n\tif (!packageJson.name) {\n\t\tthrow new Error(`${packageJsonPath} does not declare a package name.`)\n\t}\n\n\tconst resolveDirectory = path.dirname(packageJsonPath)\n\tconst peerDependencies = Object.keys(packageJson.peerDependencies ?? {})\n\tconst external = unique([...peerDependencies, ...(config.external ?? [])])\n\tconst exportImports = selectExportImports(\n\t\tpackageJson.name,\n\t\tpackageJson.exports,\n\t\tconfig.exports,\n\t)\n\n\tconst { exportRows, recipeRows } = await withPackageResolver(\n\t\tpackageJson.name,\n\t\tresolveDirectory,\n\t\tasync (packageResolver) => {\n\t\t\tconst exportRows = await Promise.all(\n\t\t\t\texportImports.map(async (specifier): Promise<TonnageRow> => ({\n\t\t\t\t\t...(await measureImportsWithPlugins([specifier], {\n\t\t\t\t\t\texternal,\n\t\t\t\t\t\tplatform: config.platform,\n\t\t\t\t\t\tplugins: [packageResolver],\n\t\t\t\t\t\tresolveDirectory,\n\t\t\t\t\t\ttarget: config.target,\n\t\t\t\t\t})),\n\t\t\t\t\timports: [specifier],\n\t\t\t\t\tname: specifier,\n\t\t\t\t})),\n\t\t\t)\n\t\t\tconst recipeRows = await Promise.all(\n\t\t\t\t(config.recipes ?? []).map(async (recipe) => ({\n\t\t\t\t\t...(await measureEntryWithPlugins(recipe.entry, {\n\t\t\t\t\t\texternal: unique([...external, ...(recipe.external ?? [])]),\n\t\t\t\t\t\tplatform: recipe.platform ?? config.platform,\n\t\t\t\t\t\tplugins: [packageResolver],\n\t\t\t\t\t\tresolveDirectory,\n\t\t\t\t\t\ttarget: config.target,\n\t\t\t\t\t})),\n\t\t\t\t\tentry: recipe.entry,\n\t\t\t\t\tname: recipe.name,\n\t\t\t\t})),\n\t\t\t)\n\n\t\t\treturn { exportRows, recipeRows }\n\t\t},\n\t)\n\n\treturn {\n\t\texports: exportRows,\n\t\tpackageName: packageJson.name,\n\t\trecipes: recipeRows,\n\t}\n}\n\nexport function renderTonnageMarkdown(\n\treport: TonnageReport,\n\tconfig: TonnageConfig = {},\n): string {\n\tconst hasExports = report.exports.length > 0\n\tconst hasRecipes = report.recipes.length > 0\n\tconst hasMultipleSections = hasExports && hasRecipes\n\tconst lines = [\n\t\t`## ${config.heading ?? `Bundle size`}`,\n\t\t``,\n\t\t...methodologyLines({ hasExports, hasRecipes }),\n\t]\n\n\tif (hasExports) {\n\t\tlines.push(\n\t\t\t``,\n\t\t\t...(hasMultipleSections ? [`### Package exports`, ``] : []),\n\t\t\t...markdownTable(\n\t\t\t\t[`Import`, `Minified JS`, `Gzip JS`],\n\t\t\t\treport.exports.map((row) => [\n\t\t\t\t\tcode(row.name),\n\t\t\t\t\tformatBytes(row.rawBytes),\n\t\t\t\t\tformatBytes(row.gzipBytes),\n\t\t\t\t]),\n\t\t\t\t[`left`, `right`, `right`],\n\t\t\t),\n\t\t)\n\t}\n\n\tif (hasRecipes) {\n\t\tlines.push(\n\t\t\t``,\n\t\t\t...(hasMultipleSections ? [`### Usage examples`, ``] : []),\n\t\t\t...markdownTable(\n\t\t\t\t[`Recipe`, `Entry`, `Minified JS`, `Gzip JS`],\n\t\t\t\treport.recipes.map((row) => [\n\t\t\t\t\tescapeCell(row.name),\n\t\t\t\t\tcode(row.entry),\n\t\t\t\t\tformatBytes(row.rawBytes),\n\t\t\t\t\tformatBytes(row.gzipBytes),\n\t\t\t\t]),\n\t\t\t\t[`left`, `left`, `right`, `right`],\n\t\t\t),\n\t\t)\n\t}\n\n\treturn lines.join(`\\n`)\n}\n\nfunction methodologyLines({\n\thasExports,\n\thasRecipes,\n}: {\n\thasExports: boolean\n\thasRecipes: boolean\n}): string[] {\n\tconst lines: string[] = []\n\tif (hasExports) {\n\t\tlines.push(`Package export sizes include complete runtime export surfaces.`)\n\t}\n\tif (hasRecipes) {\n\t\tlines.push(\n\t\t\t`Usage example sizes bundle their entry files and tree-shake unused exports.`,\n\t\t)\n\t}\n\tlines.push(\n\t\t`Sizes are exact minified and level-9 gzip JavaScript byte counts. Declarations, source maps, CSS, and other assets are excluded. Peer dependencies stay external, and shared modules are counted once per bundle.`,\n\t)\n\treturn lines\n}\n\nfunction selectExportImports(\n\tpackageName: string,\n\texportsValue: unknown,\n\tselection: TonnageConfig[`exports`],\n): string[] {\n\tif (selection === false) {\n\t\treturn []\n\t}\n\n\tconst exportKeys = getExportKeys(exportsValue)\n\tconst options: TonnageExports = typeof selection === `object` ? selection : {}\n\tconst included = options.include\n\t\t? [...options.include]\n\t\t: exportKeys.filter((exportKey) => exportKey !== `./package.json`)\n\tconst excluded = new Set(options.exclude ?? [])\n\n\treturn included\n\t\t.filter((exportKey) => !excluded.has(exportKey))\n\t\t.map((exportKey) => toPackageImport(packageName, exportKey))\n}\n\nfunction getExportKeys(exportsValue: unknown): string[] {\n\tif (typeof exportsValue === `string` || Array.isArray(exportsValue)) {\n\t\treturn [`.`]\n\t}\n\tif (!exportsValue || typeof exportsValue !== `object`) {\n\t\treturn [`.`]\n\t}\n\n\tconst keys = Object.keys(exportsValue)\n\treturn keys.some((key) => key.startsWith(`.`)) ? keys : [`.`]\n}\n\nfunction toPackageImport(packageName: string, exportKey: string): string {\n\tif (exportKey === `.`) {\n\t\treturn packageName\n\t}\n\tif (!exportKey.startsWith(`./`)) {\n\t\tthrow new Error(`Invalid package export key: ${exportKey}`)\n\t}\n\treturn `${packageName}/${exportKey.slice(2)}`\n}\n\nfunction formatBytes(bytes: number): string {\n\treturn `${new Intl.NumberFormat(`en-US`).format(bytes)} B`\n}\n\nfunction code(value: string): string {\n\treturn `<code>${escapeCell(value)}</code>`\n}\n\nfunction escapeCell(value: string): string {\n\treturn value.replaceAll(`&`, `&amp;`).replaceAll(`|`, `&#124;`)\n}\n\nfunction unique(values: readonly string[]): string[] {\n\treturn [...new Set(values)]\n}\n\nfunction markdownTable(\n\theadings: readonly string[],\n\trows: readonly (readonly string[])[],\n\talignments: readonly (`left` | `right`)[],\n): string[] {\n\tconst widths = headings.map((heading, column) =>\n\t\tMath.max(heading.length, ...rows.map((row) => row[column]?.length ?? 0), 3),\n\t)\n\tconst renderRow = (cells: readonly string[]): string =>\n\t\t`| ${cells.map((cell, column) => padCell(cell, widths[column] ?? 3, alignments[column] ?? `left`)).join(` | `)} |`\n\tconst separator = widths.map((width, column) =>\n\t\talignments[column] === `right`\n\t\t\t? `${`-`.repeat(width - 1)}:`\n\t\t\t: `-`.repeat(width),\n\t)\n\n\treturn [renderRow(headings), renderRow(separator), ...rows.map(renderRow)]\n}\n\nfunction padCell(\n\tcell: string,\n\twidth: number,\n\talignment: `left` | `right`,\n): string {\n\treturn alignment === `right` ? cell.padStart(width) : cell.padEnd(width)\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport { updateGeneratedSection } from \"./readme.ts\"\nimport { createTonnageReport, renderTonnageMarkdown } from \"./report.ts\"\nimport type { TonnageConfig, TonnageMode, TonnageRunResult } from \"./types.ts\"\n\nexport async function runTonnage(\n\tconfig: TonnageConfig,\n\toptions: {\n\t\tconfigDirectory?: string\n\t\tmode: TonnageMode\n\t},\n): Promise<TonnageRunResult> {\n\tconst configDirectory = path.resolve(options.configDirectory ?? process.cwd())\n\tconst readmePath = path.resolve(configDirectory, config.readme ?? `README.md`)\n\tconst oldReadme = await fs.readFile(readmePath, `utf8`)\n\tconst report = await createTonnageReport(config, configDirectory)\n\tconst markdown = renderTonnageMarkdown(report, config)\n\tconst newReadme = updateGeneratedSection(oldReadme, markdown, config.marker)\n\tconst changed = oldReadme !== newReadme\n\n\tif (options.mode === `write` && changed) {\n\t\tawait fs.writeFile(readmePath, newReadme)\n\t}\n\n\treturn { changed, readmePath, report }\n}\n"],"mappings":"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"}