diff --git a/CHANGELOG.md b/CHANGELOG.md index 9239cdaf3..6b063d53a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Change Log v5.4.7 --- +* Fixed CLI `--config` failures hiding the real cause behind a generic `Cannot open config file` message. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1101 * Fixed `sourceMapFileName` ending in `.js.map` (e.g. `foo.min.js.map`) being mangled in the emitted `//# sourceMappingURL=` comment. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1312 * Fixed `URIError: URI malformed` crash when `stringArray` with `base64`/`rc4` encoding processed a string literal containing lone surrogate code units (e.g. `"[^\uD800-\uDFFF]"`). Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1431 diff --git a/src/cli/utils/CLIUtils.ts b/src/cli/utils/CLIUtils.ts index 4dcc4a860..dd58e54a6 100644 --- a/src/cli/utils/CLIUtils.ts +++ b/src/cli/utils/CLIUtils.ts @@ -15,26 +15,32 @@ export class CLIUtils { * @returns {TDictionary} */ public static getUserConfig(configPath: string): TDictionary { - let config: TDictionary; - const configFileExtension: string = path.extname(configPath); const isValidExtension: boolean = CLIUtils.allowedConfigFileExtensions.includes(configFileExtension); if (!isValidExtension) { - throw new ReferenceError('Given config path must be a valid `.js|.mjs|.cjs` or `.json` file path'); + throw new ReferenceError('Given config path must be a valid `.js`, `.cjs` or `.json` file path'); } + const errors: Error[] = []; + try { - config = require(configPath); - } catch { - try { - config = __non_webpack_require__(configPath); - } catch { - throw new ReferenceError(`Cannot open config file with path: ${configPath}`); - } + return require(configPath); + } catch (error) { + errors.push(error); } - return config; + try { + return __non_webpack_require__(configPath); + } catch (error) { + errors.push(error); + } + + // surface the underlying reason (invalid JSON, `ERR_REQUIRE_ESM`, missing file, ...) + // instead of masking it behind a generic message + throw new ReferenceError( + `Cannot open config file with path: ${configPath}. Reason: ${CLIUtils.getConfigErrorReason(errors)}` + ); } /** @@ -44,4 +50,20 @@ export class CLIUtils { public static stringifyOptionAvailableValues(optionEnum: TDictionary): string { return Object.values(optionEnum).join(`${StringSeparator.Comma} `); } + + /** + * @param {Error[]} errors + * @returns {string} + */ + private static getConfigErrorReason(errors: Error[]): string { + const [firstError, secondError] = errors; + + // in the webpack bundle the real reason comes from the `__non_webpack_require__` call (the second error); + // in a plain Node/ts-node context `__non_webpack_require__` is not defined, so the real reason is the first error + if (secondError && !secondError.message.includes('__non_webpack_require__')) { + return secondError.message; + } + + return firstError.message; + } } diff --git a/test/fixtures/invalid-config.json b/test/fixtures/invalid-config.json new file mode 100644 index 000000000..e2e37f3ad --- /dev/null +++ b/test/fixtures/invalid-config.json @@ -0,0 +1,5 @@ +{ + compact: true, + selfDefending: false, + sourceMap: true +} diff --git a/test/unit-tests/cli/utils/CLIUtils.spec.ts b/test/unit-tests/cli/utils/CLIUtils.spec.ts index 30d1fa6ba..6adcf3d2c 100644 --- a/test/unit-tests/cli/utils/CLIUtils.spec.ts +++ b/test/unit-tests/cli/utils/CLIUtils.spec.ts @@ -106,6 +106,27 @@ describe('CLIUtils', () => { assert.throws(testFunc, /Cannot open config file/); }); }); + + // https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1101 + describe('Variant #4: config file with invalid content', () => { + const configDirName: string = 'test/fixtures'; + const configFileName: string = 'invalid-config.json'; + const configFilePath: string = `../../../${configDirName}/${configFileName}`; + + let testFunc: () => void; + + before(() => { + testFunc = () => CLIUtils.getUserConfig(configFilePath); + }); + + it('should throw an error that includes the config file path', () => { + assert.throws(testFunc, /Cannot open config file with path/); + }); + + it('should surface the underlying reason instead of masking it', () => { + assert.throws(testFunc, /Reason:.*JSON/); + }); + }); }); describe('stringifyOptionAvailableValues', () => {