portapack 0.3.1 → 0.3.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.
Files changed (74) hide show
  1. package/.eslintrc.json +67 -8
  2. package/.releaserc.js +25 -27
  3. package/CHANGELOG.md +14 -22
  4. package/LICENSE.md +21 -0
  5. package/README.md +22 -53
  6. package/commitlint.config.js +30 -34
  7. package/dist/cli/cli-entry.cjs +183 -98
  8. package/dist/cli/cli-entry.cjs.map +1 -1
  9. package/dist/index.d.ts +0 -3
  10. package/dist/index.js +178 -97
  11. package/dist/index.js.map +1 -1
  12. package/docs/.vitepress/config.ts +38 -33
  13. package/docs/.vitepress/sidebar-generator.ts +89 -38
  14. package/docs/architecture.md +186 -0
  15. package/docs/cli.md +23 -23
  16. package/docs/code-of-conduct.md +7 -1
  17. package/docs/configuration.md +12 -11
  18. package/docs/contributing.md +6 -2
  19. package/docs/deployment.md +10 -5
  20. package/docs/development.md +8 -5
  21. package/docs/getting-started.md +13 -13
  22. package/docs/index.md +1 -1
  23. package/docs/public/android-chrome-192x192.png +0 -0
  24. package/docs/public/android-chrome-512x512.png +0 -0
  25. package/docs/public/apple-touch-icon.png +0 -0
  26. package/docs/public/favicon-16x16.png +0 -0
  27. package/docs/public/favicon-32x32.png +0 -0
  28. package/docs/public/favicon.ico +0 -0
  29. package/docs/roadmap.md +233 -0
  30. package/docs/site.webmanifest +1 -0
  31. package/docs/troubleshooting.md +12 -1
  32. package/examples/main.ts +5 -30
  33. package/examples/sample-project/script.js +1 -1
  34. package/jest.config.ts +8 -13
  35. package/nodemon.json +5 -10
  36. package/package.json +2 -5
  37. package/src/cli/cli-entry.ts +2 -2
  38. package/src/cli/cli.ts +21 -16
  39. package/src/cli/options.ts +127 -113
  40. package/src/core/bundler.ts +253 -222
  41. package/src/core/extractor.ts +632 -565
  42. package/src/core/minifier.ts +173 -162
  43. package/src/core/packer.ts +141 -137
  44. package/src/core/parser.ts +74 -73
  45. package/src/core/web-fetcher.ts +270 -258
  46. package/src/index.ts +18 -17
  47. package/src/types.ts +9 -11
  48. package/src/utils/font.ts +12 -6
  49. package/src/utils/logger.ts +110 -105
  50. package/src/utils/meta.ts +75 -76
  51. package/src/utils/mime.ts +50 -50
  52. package/src/utils/slugify.ts +33 -34
  53. package/tests/unit/cli/cli-entry.test.ts +72 -70
  54. package/tests/unit/cli/cli.test.ts +314 -278
  55. package/tests/unit/cli/options.test.ts +294 -301
  56. package/tests/unit/core/bundler.test.ts +426 -329
  57. package/tests/unit/core/extractor.test.ts +793 -549
  58. package/tests/unit/core/minifier.test.ts +374 -274
  59. package/tests/unit/core/packer.test.ts +298 -264
  60. package/tests/unit/core/parser.test.ts +538 -150
  61. package/tests/unit/core/web-fetcher.test.ts +389 -359
  62. package/tests/unit/index.test.ts +238 -197
  63. package/tests/unit/utils/font.test.ts +26 -21
  64. package/tests/unit/utils/logger.test.ts +267 -260
  65. package/tests/unit/utils/meta.test.ts +29 -28
  66. package/tests/unit/utils/mime.test.ts +73 -74
  67. package/tests/unit/utils/slugify.test.ts +14 -12
  68. package/tsconfig.build.json +9 -10
  69. package/tsconfig.jest.json +1 -1
  70. package/tsconfig.json +2 -2
  71. package/tsup.config.ts +8 -9
  72. package/typedoc.json +5 -9
  73. /package/docs/{portapack-transparent.png → public/portapack-transparent.png} +0 -0
  74. /package/docs/{portapack.jpg → public/portapack.jpg} +0 -0
package/jest.config.ts CHANGED
@@ -36,9 +36,9 @@ const config: Config = {
36
36
  'ts-jest',
37
37
  {
38
38
  // useESM: false, // Explicitly false or remove this line entirely
39
- tsconfig: './tsconfig.jest.json' // Ensure this tsconfig targets CommonJS
40
- }
41
- ]
39
+ tsconfig: './tsconfig.jest.json', // Ensure this tsconfig targets CommonJS
40
+ },
41
+ ],
42
42
  },
43
43
  /**
44
44
  * Treat `.ts` files as ESM modules.
@@ -54,7 +54,7 @@ const config: Config = {
54
54
  // Keep absolute path alias
55
55
  '^portapack(.*)$': '<rootDir>/src$1',
56
56
  // Mock path support
57
- '^__mocks__/(.*)$': '<rootDir>/__mocks__/$1'
57
+ '^__mocks__/(.*)$': '<rootDir>/__mocks__/$1',
58
58
  },
59
59
 
60
60
  /**
@@ -65,7 +65,7 @@ const config: Config = {
65
65
  '!src/**/*.d.ts',
66
66
  '!src/**/*.test.ts',
67
67
  '!src/types.ts',
68
- '!src/cli/cli-entry.ts'
68
+ '!src/cli/cli-entry.ts',
69
69
  ],
70
70
 
71
71
  /**
@@ -93,10 +93,7 @@ const config: Config = {
93
93
  /**
94
94
  * Type-ahead filters for watch mode
95
95
  */
96
- watchPlugins: [
97
- 'jest-watch-typeahead/filename',
98
- 'jest-watch-typeahead/testname'
99
- ],
96
+ watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
100
97
 
101
98
  /**
102
99
  * Don't error if no tests found (for initial development)
@@ -106,9 +103,7 @@ const config: Config = {
106
103
  /**
107
104
  * Ignore specific paths in watch mode
108
105
  */
109
- watchPathIgnorePatterns: [
110
- path.join('<rootDir>', 'tests', '__fixtures__', 'output')
111
- ],
106
+ watchPathIgnorePatterns: [path.join('<rootDir>', 'tests', '__fixtures__', 'output')],
112
107
 
113
108
  /**
114
109
  * Mock implementations
@@ -131,4 +126,4 @@ const config: Config = {
131
126
  errorOnDeprecated: true,
132
127
  };
133
128
 
134
- export default config;
129
+ export default config;
package/nodemon.json CHANGED
@@ -1,11 +1,6 @@
1
1
  {
2
- "watch": ["src"],
3
- "ext": "ts,tsx",
4
- "ignore": [
5
- "src/**/*.spec.ts",
6
- "src/**/*.test.ts",
7
- "dist",
8
- "node_modules"
9
- ],
10
- "exec": "npm run docs:api"
11
- }
2
+ "watch": ["src"],
3
+ "ext": "ts,tsx",
4
+ "ignore": ["src/**/*.spec.ts", "src/**/*.test.ts", "dist", "node_modules"],
5
+ "exec": "npm run docs:api"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portapack",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "📦 A tool to bundle and minify HTML and all its dependencies into a single portable file.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -45,13 +45,10 @@
45
45
  "portable",
46
46
  "minify",
47
47
  "packer",
48
- "assets",
49
48
  "cli",
50
49
  "offline",
51
- "webapp",
52
50
  "static-site",
53
- "bundle",
54
- "recursive"
51
+ "bundle"
55
52
  ],
56
53
  "author": "Manic.agency",
57
54
  "license": "MIT",
@@ -19,10 +19,10 @@ if (require.main === module) {
19
19
  if (stderr) process.stderr.write(stderr);
20
20
  process.exit(Number(exitCode));
21
21
  })
22
- .catch((err) => {
22
+ .catch(err => {
23
23
  console.error('💥 Unhandled CLI error:', err);
24
24
  process.exit(1);
25
25
  });
26
26
  }
27
27
 
28
- export { startCLI };
28
+ export { startCLI };
package/src/cli/cli.ts CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
+
10
11
  // Use standard require for core modules in CJS context if needed
11
12
  // const path = require('path');
12
13
  // const fs = require('fs');
@@ -23,17 +24,16 @@ import type { CLIResult, BundleOptions, BundleMetadata, CLIOptions } from '../ty
23
24
  */
24
25
  function getPackageJson(): Record<string, any> {
25
26
  try {
26
- // FIX: Use require.resolve which works in CommonJS to find the package path
27
- // It resolves relative to the location of this file or the node_modules structure
28
27
  // Assumes 'portapack' is the package name defined in package.json
29
28
  // We need the package.json itself, so resolve 'portapack/package.json'
30
29
  // Use __dirname if available in CJS context, otherwise try relative from cwd as fallback
31
- const searchPath = typeof __dirname !== 'undefined' ? path.join(__dirname, '..', '..') : process.cwd();
30
+ const searchPath =
31
+ typeof __dirname !== 'undefined' ? path.join(__dirname, '..', '..') : process.cwd();
32
32
  const pkgJsonPath = require.resolve('portapack/package.json', { paths: [searchPath] });
33
33
  return require(pkgJsonPath); // Use require directly to load JSON
34
34
  } catch (err) {
35
- console.error("Warning: Could not dynamically load package.json for version.", err); // Log error for debugging
36
- return { version: '0.0.0-unknown' };
35
+ console.error('Warning: Could not dynamically load package.json for version.', err); // Log error for debugging
36
+ return { version: '0.0.0-unknown' };
37
37
  }
38
38
  }
39
39
 
@@ -54,16 +54,21 @@ export async function runCli(argv: string[] = process.argv): Promise<CLIResult>
54
54
  const originalWarn = console.warn;
55
55
 
56
56
  const restoreConsole = () => {
57
- console.log = originalLog;
58
- console.error = originalErr;
59
- console.warn = originalWarn;
57
+ console.log = originalLog;
58
+ console.error = originalErr;
59
+ console.warn = originalWarn;
60
60
  };
61
61
 
62
- console.log = (...args) => { stdout += args.join(' ') + '\n'; };
63
- console.error = (...args) => { stderr += args.join(' ') + '\n'; };
64
- console.warn = (...args) => { stderr += args.join(' ') + '\n'; };
62
+ console.log = (...args) => {
63
+ stdout += args.join(' ') + '\n';
64
+ };
65
+ console.error = (...args) => {
66
+ stderr += args.join(' ') + '\n';
67
+ };
68
+ console.warn = (...args) => {
69
+ stderr += args.join(' ') + '\n';
70
+ };
65
71
 
66
- // FIX: Use the correct type CLIOptions which includes 'input'
67
72
  let cliOptions: CLIOptions | undefined;
68
73
  try {
69
74
  // Get the fully parsed options object which includes 'input'
@@ -83,7 +88,9 @@ export async function runCli(argv: string[] = process.argv): Promise<CLIResult>
83
88
 
84
89
  // Use path.basename and handle potential extension removal carefully
85
90
  const inputBasename = path.basename(cliOptions.input);
86
- const outputDefaultBase = inputBasename.includes('.') ? inputBasename.substring(0, inputBasename.lastIndexOf('.')) : inputBasename;
91
+ const outputDefaultBase = inputBasename.includes('.')
92
+ ? inputBasename.substring(0, inputBasename.lastIndexOf('.'))
93
+ : inputBasename;
87
94
  // Use the parsed output option or generate default
88
95
  const outputPath = cliOptions.output ?? `${outputDefaultBase || 'output'}.packed.html`;
89
96
 
@@ -105,7 +112,6 @@ export async function runCli(argv: string[] = process.argv): Promise<CLIResult>
105
112
  return { stdout, stderr, exitCode: 0 };
106
113
  }
107
114
 
108
- // FIX: Call pack with input as the first argument, and the rest of the options as the second.
109
115
  // The cliOptions object should be compatible with PackOptions expected by pack.
110
116
  const result = await pack(cliOptions.input, cliOptions);
111
117
 
@@ -129,7 +135,6 @@ export async function runCli(argv: string[] = process.argv): Promise<CLIResult>
129
135
  console.warn(` - ${err}`);
130
136
  }
131
137
  }
132
-
133
138
  } catch (err: any) {
134
139
  console.error(`\n💥 Error: ${err?.message || 'Unknown failure'}`);
135
140
  // Check verbose flag on the correct variable
@@ -147,4 +152,4 @@ export async function runCli(argv: string[] = process.argv): Promise<CLIResult>
147
152
  /**
148
153
  * Default exportable main runner for CLI invocation.
149
154
  */
150
- export const main = runCli;
155
+ export const main = runCli;
@@ -9,7 +9,6 @@ import { Command, Option } from 'commander';
9
9
  // Ensure CLIOptions is imported correctly if defined in types.ts
10
10
  import { LogLevel, type LogLevelName, type CLIOptions } from '../types';
11
11
 
12
-
13
12
  // Define valid choices for the --log-level option
14
13
  const logLevels: LogLevelName[] = ['debug', 'info', 'warn', 'error', 'silent', 'none'];
15
14
 
@@ -21,10 +20,10 @@ const logLevels: LogLevelName[] = ['debug', 'info', 'warn', 'error', 'silent', '
21
20
  * @returns {boolean | number} True if flag only/invalid number, otherwise the parsed depth.
22
21
  */
23
22
  function parseRecursiveValue(val: string | undefined): boolean | number {
24
- if (val === undefined) return true; // Flag only
25
- const parsed = parseInt(val, 10);
26
- // Invalid number (NaN) or negative depth treated as simple boolean 'true'
27
- return isNaN(parsed) || parsed < 0 ? true : parsed;
23
+ if (val === undefined) return true; // Flag only
24
+ const parsed = parseInt(val, 10);
25
+ // Invalid number (NaN) or negative depth treated as simple boolean 'true'
26
+ return isNaN(parsed) || parsed < 0 ? true : parsed;
28
27
  }
29
28
 
30
29
  /**
@@ -38,114 +37,129 @@ function parseRecursiveValue(val: string | undefined): boolean | number {
38
37
  * @throws {Error} Throws errors if Commander encounters parsing/validation issues.
39
38
  */
40
39
  export function parseOptions(argv: string[] = process.argv): CLIOptions {
41
- const program = new Command();
42
-
43
- program
44
- .name('portapack')
45
- .version('0.0.0') // Version updated dynamically by cli.ts
46
- .description('📦 Bundle HTML and its dependencies into a portable file')
47
- .argument('[input]', 'Input HTML file or URL')
48
- .option('-o, --output <file>', 'Output file path')
49
- .option('-m, --minify', 'Enable all minification (HTML, CSS, JS)') // Presence enables default true below
50
- .option('--no-minify', 'Disable all minification') // Global disable flag
51
- .option('--no-minify-html', 'Disable HTML minification')
52
- .option('--no-minify-css', 'Disable CSS minification')
53
- .option('--no-minify-js', 'Disable JavaScript minification')
54
- .option('-e, --embed-assets', 'Embed assets as data URIs') // Presence enables default true below
55
- .option('--no-embed-assets', 'Keep asset links relative/absolute') // Disable flag
56
- .option('-r, --recursive [depth]', 'Recursively crawl site (optional depth)', parseRecursiveValue)
57
- .option('--max-depth <n>', 'Set max depth for recursive crawl (alias for -r <n>)', parseInt)
58
- .option('-b, --base-url <url>', 'Base URL for resolving relative links')
59
- .option('-d, --dry-run', 'Run without writing output file')
60
- .option('-v, --verbose', 'Enable verbose (debug) logging')
61
- .addOption(new Option('--log-level <level>', 'Set logging level')
62
- .choices(logLevels));
63
-
64
- // Prevent commander from exiting on error during tests (optional)
65
- // program.exitOverride();
66
-
67
- program.parse(argv);
68
-
69
- // Raw options object from Commander's parsing
70
- const opts = program.opts<CLIOptions>();
71
- // Get the positional argument (input) if provided
72
- const inputArg = program.args.length > 0 ? program.args[0] : undefined;
73
-
74
- // --- Determine Effective LogLevel ---
75
- let finalLogLevel: LogLevel;
76
- const cliLogLevel = opts.logLevel as unknown as LogLevelName | undefined; // Commander stores choice string
77
- if (cliLogLevel) {
78
- // Map string choice to LogLevel enum value
79
- switch (cliLogLevel) {
80
- case 'debug': finalLogLevel = LogLevel.DEBUG; break;
81
- case 'info': finalLogLevel = LogLevel.INFO; break;
82
- case 'warn': finalLogLevel = LogLevel.WARN; break;
83
- case 'error': finalLogLevel = LogLevel.ERROR; break;
84
- case 'silent': case 'none': finalLogLevel = LogLevel.NONE; break;
85
- default: finalLogLevel = LogLevel.INFO; // Fallback, though choices() should prevent this
86
- }
87
- } else if (opts.verbose) {
88
- // --verbose is shorthand for debug level if --log-level not set
40
+ const program = new Command();
41
+
42
+ program
43
+ .name('portapack')
44
+ .version('0.0.0') // Version updated dynamically by cli.ts
45
+ .description('📦 Bundle HTML and its dependencies into a portable file')
46
+ .argument('[input]', 'Input HTML file or URL')
47
+ .option('-o, --output <file>', 'Output file path')
48
+ .option('-m, --minify', 'Enable all minification (HTML, CSS, JS)') // Presence enables default true below
49
+ .option('--no-minify', 'Disable all minification') // Global disable flag
50
+ .option('--no-minify-html', 'Disable HTML minification')
51
+ .option('--no-minify-css', 'Disable CSS minification')
52
+ .option('--no-minify-js', 'Disable JavaScript minification')
53
+ .option('-e, --embed-assets', 'Embed assets as data URIs') // Presence enables default true below
54
+ .option('--no-embed-assets', 'Keep asset links relative/absolute') // Disable flag
55
+ .option(
56
+ '-r, --recursive [depth]',
57
+ 'Recursively crawl site (optional depth)',
58
+ parseRecursiveValue
59
+ )
60
+ .option('--max-depth <n>', 'Set max depth for recursive crawl (alias for -r <n>)', parseInt)
61
+ .option('-b, --base-url <url>', 'Base URL for resolving relative links')
62
+ .option('-d, --dry-run', 'Run without writing output file')
63
+ .option('-v, --verbose', 'Enable verbose (debug) logging')
64
+ .addOption(new Option('--log-level <level>', 'Set logging level').choices(logLevels));
65
+
66
+ // Prevent commander from exiting on error during tests (optional)
67
+ // program.exitOverride();
68
+
69
+ program.parse(argv);
70
+
71
+ // Raw options object from Commander's parsing
72
+ const opts = program.opts<CLIOptions>();
73
+ // Get the positional argument (input) if provided
74
+ const inputArg = program.args.length > 0 ? program.args[0] : undefined;
75
+
76
+ // --- Determine Effective LogLevel ---
77
+ let finalLogLevel: LogLevel;
78
+ const cliLogLevel = opts.logLevel as unknown as LogLevelName | undefined; // Commander stores choice string
79
+ if (cliLogLevel) {
80
+ // Map string choice to LogLevel enum value
81
+ switch (cliLogLevel) {
82
+ case 'debug':
89
83
  finalLogLevel = LogLevel.DEBUG;
90
- } else {
91
- // Default log level
84
+ break;
85
+ case 'info':
92
86
  finalLogLevel = LogLevel.INFO;
87
+ break;
88
+ case 'warn':
89
+ finalLogLevel = LogLevel.WARN;
90
+ break;
91
+ case 'error':
92
+ finalLogLevel = LogLevel.ERROR;
93
+ break;
94
+ case 'silent':
95
+ case 'none':
96
+ finalLogLevel = LogLevel.NONE;
97
+ break;
98
+ default:
99
+ finalLogLevel = LogLevel.INFO; // Fallback, though choices() should prevent this
93
100
  }
94
-
95
- // --- Handle Embedding ---
96
- // Default is true. --no-embed-assets flag sets opts.embedAssets to false.
97
- // Check argv directly to ensure --no- wins regardless of order.
98
- let embedAssets = true; // Start with default
99
- if (argv.includes('--no-embed-assets')) {
100
- embedAssets = false; // Explicit negation flag takes precedence
101
- } else if (opts.embedAssets === true) {
102
- embedAssets = true; // Positive flag enables it if negation wasn't present
103
- }
104
- // If neither flag is present, it remains the default 'true'.
105
-
106
- // --- Handle Minification ---
107
- // Default to true unless specifically disabled by --no-minify-<type>
108
- let minifyHtml = opts.minifyHtml !== false;
109
- let minifyCss = opts.minifyCss !== false;
110
- let minifyJs = opts.minifyJs !== false;
111
-
112
- // Global --no-minify flag overrides all individual settings
113
- // Commander sets opts.minify to false if --no-minify is used.
114
- if (opts.minify === false) {
115
- minifyHtml = false;
116
- minifyCss = false;
117
- minifyJs = false;
118
- }
119
- // Note: Positive flags (-m or individual --minify-<type>) don't need extra handling
120
- // as the initial state is true, and negations correctly turn them off.
121
-
122
- // --- Handle Recursive/MaxDepth ---
123
- // Start with the value parsed from -r/--recursive
124
- let recursiveOpt = opts.recursive;
125
- // If --max-depth was provided and is a valid non-negative number, it overrides -r
126
- if (opts.maxDepth !== undefined && !isNaN(opts.maxDepth) && opts.maxDepth >= 0) {
127
- recursiveOpt = opts.maxDepth;
128
- }
129
-
130
- // Return the final structured options object
131
- return {
132
- // Pass through directly parsed options
133
- baseUrl: opts.baseUrl,
134
- dryRun: opts.dryRun ?? false, // Ensure boolean, default false
135
- output: opts.output,
136
- verbose: opts.verbose ?? false, // Ensure boolean, default false
137
-
138
- // Set calculated/processed options
139
- input: inputArg,
140
- logLevel: finalLogLevel,
141
- recursive: recursiveOpt, // Final calculated value for recursion
142
- embedAssets: embedAssets, // Final calculated value
143
- minifyHtml: minifyHtml, // Final calculated value
144
- minifyCss: minifyCss, // Final calculated value
145
- minifyJs: minifyJs, // Final calculated value
146
-
147
- // Exclude intermediate commander properties like:
148
- // minify, logLevel (string version), maxDepth,
149
- // minifyHtml, minifyCss, minifyJs (commander's raw boolean flags)
150
- };
151
- }
101
+ } else if (opts.verbose) {
102
+ // --verbose is shorthand for debug level if --log-level not set
103
+ finalLogLevel = LogLevel.DEBUG;
104
+ } else {
105
+ // Default log level
106
+ finalLogLevel = LogLevel.INFO;
107
+ }
108
+
109
+ // --- Handle Embedding ---
110
+ // Default is true. --no-embed-assets flag sets opts.embedAssets to false.
111
+ // Check argv directly to ensure --no- wins regardless of order.
112
+ let embedAssets = true; // Start with default
113
+ if (argv.includes('--no-embed-assets')) {
114
+ embedAssets = false; // Explicit negation flag takes precedence
115
+ } else if (opts.embedAssets === true) {
116
+ embedAssets = true; // Positive flag enables it if negation wasn't present
117
+ }
118
+ // If neither flag is present, it remains the default 'true'.
119
+
120
+ // --- Handle Minification ---
121
+ // Default to true unless specifically disabled by --no-minify-<type>
122
+ let minifyHtml = opts.minifyHtml !== false;
123
+ let minifyCss = opts.minifyCss !== false;
124
+ let minifyJs = opts.minifyJs !== false;
125
+
126
+ // Global --no-minify flag overrides all individual settings
127
+ // Commander sets opts.minify to false if --no-minify is used.
128
+ if (opts.minify === false) {
129
+ minifyHtml = false;
130
+ minifyCss = false;
131
+ minifyJs = false;
132
+ }
133
+ // Note: Positive flags (-m or individual --minify-<type>) don't need extra handling
134
+ // as the initial state is true, and negations correctly turn them off.
135
+
136
+ // --- Handle Recursive/MaxDepth ---
137
+ // Start with the value parsed from -r/--recursive
138
+ let recursiveOpt = opts.recursive;
139
+ // If --max-depth was provided and is a valid non-negative number, it overrides -r
140
+ if (opts.maxDepth !== undefined && !isNaN(opts.maxDepth) && opts.maxDepth >= 0) {
141
+ recursiveOpt = opts.maxDepth;
142
+ }
143
+
144
+ // Return the final structured options object
145
+ return {
146
+ // Pass through directly parsed options
147
+ baseUrl: opts.baseUrl,
148
+ dryRun: opts.dryRun ?? false, // Ensure boolean, default false
149
+ output: opts.output,
150
+ verbose: opts.verbose ?? false, // Ensure boolean, default false
151
+
152
+ // Set calculated/processed options
153
+ input: inputArg,
154
+ logLevel: finalLogLevel,
155
+ recursive: recursiveOpt, // Final calculated value for recursion
156
+ embedAssets: embedAssets, // Final calculated value
157
+ minifyHtml: minifyHtml, // Final calculated value
158
+ minifyCss: minifyCss, // Final calculated value
159
+ minifyJs: minifyJs, // Final calculated value
160
+
161
+ // Exclude intermediate commander properties like:
162
+ // minify, logLevel (string version), maxDepth,
163
+ // minifyHtml, minifyCss, minifyJs (commander's raw boolean flags)
164
+ };
165
+ }