Skip to content

Commit 42ebdfa

Browse files
johnjenkinsJohn Jenkins
andauthored
fix(cli): --stats accepts optional path string as per documentation (#6524)
Co-authored-by: John Jenkins <john.jenkins@nanoporetech.com>
1 parent cc12853 commit 42ebdfa

File tree

5 files changed

+69
-10
lines changed

5 files changed

+69
-10
lines changed

src/cli/config-flags.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export const BOOLEAN_CLI_FLAGS = [
2929
'skipNodeCheck',
3030
'spec',
3131
'ssr',
32-
'stats',
3332
'updateScreenshot',
3433
'verbose',
3534
'version',
@@ -181,6 +180,12 @@ export const BOOLEAN_STRING_CLI_FLAGS = [
181180
* {@see https://developer.chrome.com/blog/chrome-headless-shell/}
182181
*/
183182
'headless',
183+
/**
184+
* `stats` is an argument that can optionally accept a file path where stats should be written.
185+
* When used as a boolean (--stats), it defaults to 'stencil-stats.json'.
186+
* When used with a path (--stats dist/stats.json), it writes to that path.
187+
*/
188+
'stats',
184189
] as const;
185190

186191
/**

src/cli/task-help.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const taskHelp = async (flags: ConfigFlags, logger: d.Logger, sys: d.Comp
2323
${logger.cyan('--prerender')} ${logger.dim('.......')} Prerender the application
2424
${logger.cyan('--docs')} ${logger.dim('............')} Generate component readme.md docs
2525
${logger.cyan('--config')} ${logger.dim('..........')} Set stencil config file
26-
${logger.cyan('--stats')} ${logger.dim('...........')} Write stencil-stats.json file
26+
${logger.cyan('--stats')} ${logger.dim('...........')} Write stats, optional file path (default: stencil-stats.json)
2727
${logger.cyan('--log')} ${logger.dim('.............')} Write stencil-build.log file
2828
${logger.cyan('--debug')} ${logger.dim('...........')} Set the log level to debug
2929

src/cli/test/parse-flags.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,14 @@ describe('parseFlags', () => {
144144
it('parses a boolean-string flag as a boolean with no arg', () => {
145145
const args = [`--${cliArg}`];
146146
const flags = parseFlags(args);
147-
expect(flags.headless).toBe(true);
147+
expect(flags[cliArg]).toBe(true);
148148
expect(flags.knownArgs).toEqual([`--${cliArg}`]);
149149
});
150150

151151
it(`parses a boolean-string flag as a falsy boolean with "no" arg - --no-${cliArg}`, () => {
152152
const args = [`--no-${cliArg}`];
153153
const flags = parseFlags(args);
154-
expect(flags.headless).toBe(false);
154+
expect(flags[cliArg]).toBe(false);
155155
expect(flags.knownArgs).toEqual([`--no-${cliArg}`]);
156156
});
157157

@@ -160,21 +160,21 @@ describe('parseFlags', () => {
160160
}`, () => {
161161
const negativeFlag = '--no' + cliArg.charAt(0).toUpperCase() + cliArg.slice(1);
162162
const flags = parseFlags([negativeFlag]);
163-
expect(flags.headless).toBe(false);
163+
expect(flags[cliArg]).toBe(false);
164164
expect(flags.knownArgs).toEqual([negativeFlag]);
165165
});
166166

167167
it('parses a boolean-string flag as a string with a string arg', () => {
168168
const args = [`--${cliArg}`, 'shell'];
169169
const flags = parseFlags(args);
170-
expect(flags.headless).toBe('shell');
171-
expect(flags.knownArgs).toEqual(['--headless', 'shell']);
170+
expect(flags[cliArg]).toBe('shell');
171+
expect(flags.knownArgs).toEqual([`--${cliArg}`, 'shell']);
172172
});
173173

174174
it('parses a boolean-string flag as a string with a string arg using equality', () => {
175175
const args = [`--${cliArg}=shell`];
176176
const flags = parseFlags(args);
177-
expect(flags.headless).toBe('shell');
177+
expect(flags[cliArg]).toBe('shell');
178178
expect(flags.knownArgs).toEqual([`--${cliArg}`, 'shell']);
179179
});
180180
});

src/compiler/config/outputs/validate-stats.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@ export const validateStats = (userConfig: d.ValidatedConfig, userOutputs: d.Outp
99
if (userConfig.flags.stats) {
1010
const hasOutputTarget = userOutputs.some(isOutputTargetStats);
1111
if (!hasOutputTarget) {
12-
outputTargets.push({
12+
const statsOutput: d.OutputTargetStats = {
1313
type: STATS,
14-
});
14+
};
15+
16+
// If --stats was provided with a path (string), use it; otherwise use default
17+
if (typeof userConfig.flags.stats === 'string') {
18+
statsOutput.file = userConfig.flags.stats;
19+
}
20+
21+
outputTargets.push(statsOutput);
1522
}
1623
}
1724

src/compiler/config/test/validate-stats.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,51 @@ describe('validateStats', () => {
5050
const { config } = validateConfig(userConfig, mockLoadConfigInit());
5151
expect(config.outputTargets.some((o) => o.type === 'stats')).toBe(false);
5252
});
53+
54+
it('adds stats from flags with custom path string', () => {
55+
// Test --stats dist/stats.json behavior
56+
userConfig.flags!.stats = 'dist/custom-stats.json';
57+
58+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
59+
const o = config.outputTargets.find((o) => o.type === 'stats') as d.OutputTargetStats;
60+
expect(o).toBeDefined();
61+
expect(o.file).toContain('dist/custom-stats.json');
62+
});
63+
64+
it('adds stats from flags with custom path (absolute)', () => {
65+
// Test --stats /tmp/stats.json behavior
66+
userConfig.flags!.stats = '/tmp/absolute-stats.json';
67+
68+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
69+
const o = config.outputTargets.find((o) => o.type === 'stats') as d.OutputTargetStats;
70+
expect(o).toBeDefined();
71+
expect(o.file).toBe('/tmp/absolute-stats.json');
72+
});
73+
74+
it('flags stats path takes precedence over default when no outputTarget', () => {
75+
// When --stats has a path, it should be used instead of the default
76+
userConfig.flags!.stats = 'custom-location/stats.json';
77+
78+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
79+
const o = config.outputTargets.find((o) => o.type === 'stats') as d.OutputTargetStats;
80+
expect(o).toBeDefined();
81+
expect(o.file).toContain('custom-location/stats.json');
82+
expect(o.file).not.toContain('stencil-stats.json');
83+
});
84+
85+
it('does not override existing stats outputTarget when flag has path', () => {
86+
// When there's already a stats outputTarget in config, flag should not add another
87+
userConfig.outputTargets = [
88+
{
89+
type: 'stats',
90+
file: 'config-defined.json',
91+
} as d.OutputTargetStats,
92+
];
93+
userConfig.flags!.stats = 'flag-defined.json';
94+
95+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
96+
const statsTargets = config.outputTargets.filter((o) => o.type === 'stats');
97+
expect(statsTargets.length).toBe(1);
98+
expect(statsTargets[0].file).toContain('config-defined.json');
99+
});
53100
});

0 commit comments

Comments
 (0)