portapack 0.3.0 → 0.3.2
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/.eslintrc.json +67 -8
- package/.github/workflows/ci.yml +5 -4
- package/.releaserc.js +25 -27
- package/CHANGELOG.md +12 -19
- package/LICENSE.md +21 -0
- package/README.md +34 -36
- package/commitlint.config.js +30 -34
- package/dist/cli/cli-entry.cjs +199 -135
- package/dist/cli/cli-entry.cjs.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js +194 -134
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.ts +36 -34
- package/docs/.vitepress/sidebar-generator.ts +89 -38
- package/docs/cli.md +29 -82
- package/docs/code-of-conduct.md +7 -1
- package/docs/configuration.md +103 -117
- package/docs/contributing.md +6 -2
- package/docs/deployment.md +10 -5
- package/docs/development.md +8 -5
- package/docs/getting-started.md +76 -45
- package/docs/index.md +1 -1
- package/docs/public/android-chrome-192x192.png +0 -0
- package/docs/public/android-chrome-512x512.png +0 -0
- package/docs/public/apple-touch-icon.png +0 -0
- package/docs/public/favicon-16x16.png +0 -0
- package/docs/public/favicon-32x32.png +0 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/site.webmanifest +1 -0
- package/docs/troubleshooting.md +12 -1
- package/examples/main.ts +7 -10
- package/examples/sample-project/script.js +1 -1
- package/jest.config.ts +8 -13
- package/nodemon.json +5 -10
- package/package.json +2 -5
- package/src/cli/cli-entry.ts +2 -2
- package/src/cli/cli.ts +21 -16
- package/src/cli/options.ts +127 -113
- package/src/core/bundler.ts +254 -221
- package/src/core/extractor.ts +639 -520
- package/src/core/minifier.ts +173 -162
- package/src/core/packer.ts +141 -137
- package/src/core/parser.ts +74 -73
- package/src/core/web-fetcher.ts +270 -258
- package/src/index.ts +18 -17
- package/src/types.ts +9 -11
- package/src/utils/font.ts +12 -6
- package/src/utils/logger.ts +110 -105
- package/src/utils/meta.ts +75 -76
- package/src/utils/mime.ts +50 -50
- package/src/utils/slugify.ts +33 -34
- package/tests/unit/cli/cli-entry.test.ts +72 -70
- package/tests/unit/cli/cli.test.ts +314 -278
- package/tests/unit/cli/options.test.ts +294 -301
- package/tests/unit/core/bundler.test.ts +426 -329
- package/tests/unit/core/extractor.test.ts +828 -380
- package/tests/unit/core/minifier.test.ts +374 -274
- package/tests/unit/core/packer.test.ts +298 -264
- package/tests/unit/core/parser.test.ts +538 -150
- package/tests/unit/core/web-fetcher.test.ts +389 -359
- package/tests/unit/index.test.ts +238 -197
- package/tests/unit/utils/font.test.ts +26 -21
- package/tests/unit/utils/logger.test.ts +267 -260
- package/tests/unit/utils/meta.test.ts +29 -28
- package/tests/unit/utils/mime.test.ts +73 -74
- package/tests/unit/utils/slugify.test.ts +14 -12
- package/tsconfig.build.json +9 -10
- package/tsconfig.jest.json +2 -1
- package/tsconfig.json +2 -2
- package/tsup.config.ts +8 -8
- package/typedoc.json +5 -9
- package/docs/demo.md +0 -46
- /package/docs/{portapack-transparent.png → public/portapack-transparent.png} +0 -0
- /package/docs/{portapack.jpg → public/portapack.jpg} +0 -0
package/examples/main.ts
CHANGED
@@ -13,7 +13,7 @@ import {
|
|
13
13
|
generatePortableHTML,
|
14
14
|
bundleMultiPageHTML,
|
15
15
|
generateRecursivePortableHTML,
|
16
|
-
fetchAndPackWebPage
|
16
|
+
fetchAndPackWebPage,
|
17
17
|
} from '../src/index'; // 🔧 use '../src/index' for dev, '../dist/index' for built
|
18
18
|
|
19
19
|
const TEMP_DIR = path.join(os.tmpdir(), 'portapack-example');
|
@@ -40,10 +40,7 @@ function logMetadata(meta: any, filePath: string) {
|
|
40
40
|
}
|
41
41
|
|
42
42
|
// Accepts a function that returns { html, metadata }
|
43
|
-
async function timedBundle(
|
44
|
-
name: string,
|
45
|
-
task: () => Promise<{ html: string; metadata: any }>
|
46
|
-
) {
|
43
|
+
async function timedBundle(name: string, task: () => Promise<{ html: string; metadata: any }>) {
|
47
44
|
const start = Date.now();
|
48
45
|
console.log(chalk.cyanBright(`\n⏳ ${name}`));
|
49
46
|
|
@@ -66,7 +63,7 @@ async function timedBundle(
|
|
66
63
|
embedAssets: true,
|
67
64
|
minifyHtml: true,
|
68
65
|
minifyCss: true,
|
69
|
-
minifyJs: true
|
66
|
+
minifyJs: true,
|
70
67
|
})
|
71
68
|
);
|
72
69
|
|
@@ -85,7 +82,7 @@ async function timedBundle(
|
|
85
82
|
await timedBundle('Multi-Page Site Bundling', async () => {
|
86
83
|
const pages = [
|
87
84
|
{ url: 'https://example.com', html: '<html><body>Page 1</body></html>' },
|
88
|
-
{ url: 'https://example.com/about', html: '<html><body>Page 2</body></html>' }
|
85
|
+
{ url: 'https://example.com/about', html: '<html><body>Page 2</body></html>' },
|
89
86
|
];
|
90
87
|
const html = bundleMultiPageHTML(pages);
|
91
88
|
return {
|
@@ -94,8 +91,8 @@ async function timedBundle(
|
|
94
91
|
input: 'manual pages',
|
95
92
|
pagesBundled: pages.length,
|
96
93
|
outputSize: html.length,
|
97
|
-
buildTimeMs: 0
|
98
|
-
}
|
94
|
+
buildTimeMs: 0,
|
95
|
+
},
|
99
96
|
};
|
100
97
|
});
|
101
98
|
|
@@ -107,7 +104,7 @@ async function timedBundle(
|
|
107
104
|
// 🔹 Broken page test
|
108
105
|
console.log(chalk.cyan('\n⏳ Broken Page Test'));
|
109
106
|
try {
|
110
|
-
const { html, metadata} = await fetchAndPackWebPage('https://example.com/404');
|
107
|
+
const { html, metadata } = await fetchAndPackWebPage('https://example.com/404');
|
111
108
|
const brokenOut = await writeTempFile('broken-page.html', html);
|
112
109
|
console.log(chalk.yellow('⚠️ Page returned something, saved to:'), `file://${brokenOut}`);
|
113
110
|
} catch {
|
@@ -1 +1 @@
|
|
1
|
-
console.log('hello');
|
1
|
+
console.log('hello');
|
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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.
|
3
|
+
"version": "0.3.2",
|
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",
|
package/src/cli/cli-entry.ts
CHANGED
@@ -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(
|
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 =
|
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
|
-
|
36
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
57
|
+
console.log = originalLog;
|
58
|
+
console.error = originalErr;
|
59
|
+
console.warn = originalWarn;
|
60
60
|
};
|
61
61
|
|
62
|
-
console.log = (...args) => {
|
63
|
-
|
64
|
-
|
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('.')
|
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;
|
package/src/cli/options.ts
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
91
|
-
|
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
|
-
//
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
+
}
|