diff --git a/packages/cli/src/commands/config-payments.ts b/packages/cli/src/commands/config-payments.ts index 9c089455..f2b45c7f 100644 --- a/packages/cli/src/commands/config-payments.ts +++ b/packages/cli/src/commands/config-payments.ts @@ -13,7 +13,8 @@ export type PaymentsSummary = { }; export function parsePaymentsSummary(source: string, path = 'sh1pt.config.ts'): PaymentsSummary | undefined { - const payments = readObjectBody(source, 'payments'); + const uncommented = maskComments(source); + const payments = readObjectBody(uncommented, 'payments'); if (!payments) return undefined; const providers = readObjectBody(payments, 'providers'); const defaultProvider = readStringProperty(payments, 'defaultProvider'); @@ -31,6 +32,64 @@ export function parsePaymentsSummary(source: string, path = 'sh1pt.config.ts'): }; } +function maskComments(source: string): string { + let result = ''; + let quote: '"' | "'" | '`' | undefined; + + for (let i = 0; i < source.length; i += 1) { + const ch = source[i]!; + const next = source[i + 1]; + + if (quote) { + result += ch; + if (ch === quote && !isEscaped(source, i)) quote = undefined; + continue; + } + + if (ch === '"' || ch === "'" || ch === '`') { + quote = ch; + result += ch; + continue; + } + + if (ch === '/' && next === '/') { + result += ' '; + i += 2; + while (i < source.length && source[i] !== '\n' && source[i] !== '\r') { + result += ' '; + i += 1; + } + if (i < source.length) result += source[i]; + continue; + } + + if (ch === '/' && next === '*') { + result += ' '; + i += 2; + while (i < source.length) { + if (source[i] === '*' && source[i + 1] === '/') { + result += ' '; + i += 1; + break; + } + result += source[i] === '\n' || source[i] === '\r' ? source[i] : ' '; + i += 1; + } + continue; + } + + result += ch; + } + + return result; +} + +function isEscaped(source: string, index: number): boolean { + let slashCount = 0; + for (let i = index - 1; i >= 0 && source[i] === '\\'; i -= 1) slashCount += 1; + return slashCount % 2 === 1; +} + function readObjectBody(source: string, property: string): string | undefined { const match = new RegExp(`(?:^|[,{\\s])${propertyKeyPattern(property)}\\s*:`).exec(source); if (!match) return undefined; diff --git a/packages/cli/src/commands/config.test.ts b/packages/cli/src/commands/config.test.ts index 4a80be04..4887e57d 100644 --- a/packages/cli/src/commands/config.test.ts +++ b/packages/cli/src/commands/config.test.ts @@ -36,6 +36,41 @@ describe('parsePaymentsSummary', () => { expect(parsePaymentsSummary('export default defineConfig({ name: "demo" })')).toBeUndefined(); }); + it('ignores payments-shaped examples inside comments', () => { + const source = ` + export default defineConfig({ + // payments: { providers: { fake: { use: 'payment-fake' } } } + /* + payments: { + defaultProvider: 'payment-commented', + providers: { commented: { use: 'payment-commented' } }, + }, + */ + name: 'demo', + }); + `; + + expect(parsePaymentsSummary(source)).toBeUndefined(); + }); + + it('does not let a commented example shadow the real payments block', () => { + const summary = parsePaymentsSummary(` + export default defineConfig({ + // payments: { providers: { fake: { use: 'payment-fake' } } } + payments: { + defaultProvider: 'coinpay', + providers: { + coinpay: { use: 'payment-coinpay' }, + }, + }, + }); + `); + + expect(summary?.providers).toEqual([ + { key: 'coinpay', use: 'payment-coinpay', enabled: true, isDefault: true }, + ]); + }); + it('extracts payments when config object keys are quoted', () => { const summary = parsePaymentsSummary(` export default defineConfig({