Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: restore stats output target functionality. #3030

Merged
merged 12 commits into from
Sep 24, 2021

Conversation

splitinfinities
Copy link
Contributor

@splitinfinities splitinfinities commented Aug 26, 2021

Pull request checklist

Please check if your PR fulfills the following requirements:

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed (for bug fixes / features)
  • Build (npm run build) was run locally and any changes were pushed
  • Unit tests (npm test) were run locally and passed
  • E2E Tests (npm run test.karma.prod) were run locally and passed
  • Prettier (npm run format) was run locally and passed

Pull request type

Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

What is the current behavior?

When the stats flag, or the stats output target was added to a consumers repo, the stencil-stats.json file was not generated.

GitHub Issue Number: #2926
Jira: STENCIL-43

What is the new behavior?

This file, when generated, provides a json file that folks can use in order to pull in what files were generated and how large those files were, as well as some of the configuration options passed to the compiler when it was invoked.

Does this introduce a breaking change?

  • Yes
  • No

Testing

Go check this app out: https://github.com/splitinfinities/stencil-build-stats

As well as this branch. Link them, and build. You'll see the stencil-stats.json file produce changes.

Other information

Stencil Site related PR: https://github.com/ionic-team/stencil-site/pull/773/files

src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
@@ -26,7 +26,7 @@ export const generateSystem = async (
const results = await generateRollupOutput(rollupBuild, esmOpts, config, buildCtx.entryModules);
if (results != null) {
const destinations = systemOutputs.map((o) => o.esmDir);
await generateLazyModules(
buildCtx.systemComponentBundle = await generateLazyModules(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar scenario to src/compiler/output-targets/dist-lazy/generate-cjs.ts

@@ -60,15 +60,13 @@ export const outputLazy = async (config: d.Config, compilerCtx: d.CompilerCtx, b

const rollupBuild = await bundleOutput(config, compilerCtx, buildCtx, bundleOpts);
if (rollupBuild != null) {
const [componentBundle] = await Promise.all([
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Within this promise, the return signature in generateEsmBrowser was used and the others returned undefined. Refactoring to reset the buildCtx where the function does internal modifications felt like a better fit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will effectively serializes these outputs - do we have any measurements on how that impacts performance of the lazy build? If not, can we generate some?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lars generated some, but I'll write a node script to run a build 10 times to gather and report data.

src/declarations/stencil-private.ts Outdated Show resolved Hide resolved
@@ -260,7 +266,8 @@ export type BuildTask = any;

export type BuildStatus = 'pending' | 'error' | 'disabled' | 'default';

export interface BuildStats {
export interface CompilerBuildStats {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this name because it would match the names of other typings. Also, it wasn't used anywhere.

src/declarations/stencil-private.ts Outdated Show resolved Hide resolved
*/
export function generateBuildStats(config: d.Config, buildCtx: d.BuildCtx) {
const buildResults = buildCtx.buildResults;
let jsonData: any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to avoid adding any types if we can. This variable and the return of this function could be CompilerBuildStats | { diagnostics: d.Diagnostic[] }. You could add a type for the union and/or { diagnostics: d.Diagnostic[] }.

Comment on lines 205 to 212
esmBrowserComponentBundle: BundleModule[];
esmComponentBundle: BundleModule[];
es5ComponentBundle: BundleModule[];
systemComponentBundle: BundleModule[];
commonJsComponentBundle: BundleModule[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was worried that adding these to the build context would increase the memory usage significantly. Luckily that is not the case. I ran 10 builds of ionic-framework using master and this PR. The increase is relatively minor:

Build # master #3030
1 MEM: 682.1MB MEM: 658.6MB
2 MEM: 743.8MB MEM: 677.3MB
3 MEM: 546.5MB MEM: 757.6MB
4 MEM: 727.2MB MEM: 697.9MB
5 MEM: 731.8MB MEM: 658.1MB
6 MEM: 730.2MB MEM: 656.2MB
7 MEM: 738.5MB MEM: 749.1MB
8 MEM: 726.2MB MEM: 655.7MB
9 MEM: 709.2MB MEM: 684.9MB
10 MEM: 663.2MB MEM: 617.7MB
Average MEM: 681.3MB MEM: 699.9MB

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ltm is there a process you used for this that we could doc/reproduce in the future? I could see this also being useful for build times as well

imports: bundle.rollupResult.imports,
// code: bundle.rollupResult.code, // (use this to debug)
// Currently, this number is inaccurate vs what seems to be on disk.
byteSize: byteSize(bundle.rollupResult.code),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rwaskiewicz or @Lars, any recs about how to get this number to be more accurate without reading the file from disk? Should I omit for the time being?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How inaccurate are we talking? Like an order of magnitude? or a few bytes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, duh. It's because the files are compressed. It's a symptom of uglifying the files being applied after the build. Ironically, there's also some larger discrepancy between esmBrowser and esm as a result of esm not being minified. Since esm is not compressed right now (there's another bug somewhere filed about this...) the esm file's byte sizes produced at this point are accurate.

I think I'm going to rename this to "originalByteSize" instead. We should in the future figure out how to add that. Feels like an ordering problem. I'd hate to read files, as generating the stats may be much slower.

@splitinfinities splitinfinities marked this pull request as ready for review September 9, 2021 18:39
@splitinfinities splitinfinities requested a review from a team September 9, 2021 18:39
@splitinfinities
Copy link
Contributor Author

Note: I'm not quite sure how best to test this exclusively within Jest, considering I don't know how to mock components to be compiled as a part of the compilerCtx/buildCtx during the Jest process. If you have any ideas don't hesitate to point me in the right direction!

Copy link
Member

@rwaskiewicz rwaskiewicz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any plans to doc this as well? I noticed the checkbox in the PR summary was unchecked ATM

import type * as d from '../../declarations';

/**
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, the JSDocs in this file just seem to be stubbed out

src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
@@ -25,7 +25,8 @@ export const generateCjs = async (
const results = await generateRollupOutput(rollupBuild, esmOpts, config, buildCtx.entryModules);
if (results != null) {
const destinations = cjsOutputs.map((o) => o.cjsDir);
await generateLazyModules(

buildCtx.commonJsComponentBundle = await generateLazyModules(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update the signature of the function to reflect we return something please? (and in the other files where we generate other module outputs for lazy)?

@@ -60,15 +60,13 @@ export const outputLazy = async (config: d.Config, compilerCtx: d.CompilerCtx, b

const rollupBuild = await bundleOutput(config, compilerCtx, buildCtx, bundleOpts);
if (rollupBuild != null) {
const [componentBundle] = await Promise.all([
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will effectively serializes these outputs - do we have any measurements on how that impacts performance of the lazy build? If not, can we generate some?

Comment on lines 205 to 212
esmBrowserComponentBundle: BundleModule[];
esmComponentBundle: BundleModule[];
es5ComponentBundle: BundleModule[];
systemComponentBundle: BundleModule[];
commonJsComponentBundle: BundleModule[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ltm is there a process you used for this that we could doc/reproduce in the future? I could see this also being useful for build times as well

src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
src/compiler/build/build-stats.ts Outdated Show resolved Hide resolved
imports: bundle.rollupResult.imports,
// code: bundle.rollupResult.code, // (use this to debug)
// Currently, this number is inaccurate vs what seems to be on disk.
byteSize: byteSize(bundle.rollupResult.code),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How inaccurate are we talking? Like an order of magnitude? or a few bytes?

@splitinfinities
Copy link
Contributor Author

@rwaskiewicz Added your feedback! Had a couple conversation starters in your comments. Also, re: docs, yes I'd love to add them, but I don't think they should yet block this from being merged. I'd like to collaborate with Anthony on the content of the page. Something for next sprint I imagine.

const result = await config.sys.writeFile(outputTarget.file, JSON.stringify(data, null, 2));

if (result.error) {
config.logger.warn([`Stats failed to write file to ${outputTarget.file}: ${result.error}`]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we verified this won't print

Stats failed to write file to [Object object]: [Object object]

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file should work fine, I removed the result.error so we can guard against that.

* @param compilerCtx
* @param buildCtx
* @returns
* @param config the project build configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, the summary is missing in these JSDocs

@rwaskiewicz
Copy link
Member

@splitinfinities regarding doc: I don't think we can merge this without docs. I think that needs to be a part of our definition of done

@splitinfinities splitinfinities added Resolution: Refine This PR is marked for Jira refinement. We're not working on it - we're talking it through. Resolution: Backlog and removed Resolution: Refine This PR is marked for Jira refinement. We're not working on it - we're talking it through. labels Sep 16, 2021
src/declarations/stencil-private.ts Outdated Show resolved Hide resolved
Comment on lines +27 to +29
if (result.hasOwnProperty('compiler') && result.compiler.hasOwnProperty('version')) {
delete result.compiler.version;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What value was this producing that we had to delete it and remove it from the assertion below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version that's built at the moment of the test's build (it appended the date and time to the version)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, what was it that we changed here that made this suddenly fail?

Copy link
Contributor Author

@splitinfinities splitinfinities Sep 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It had been failing all along, actually, I just hadn't realized it because I didn't look into the build task on Github. My local build version wasn't failing because the timestamp hadn't changed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess more concretely, what did we change in this PR to cause this to fail? Or are you saying it was failing since we turned tests back on in CI?

Copy link
Contributor Author

@splitinfinities splitinfinities Sep 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean quite literally that all the tests in all of my previous commits failed on CI because the built compiler's version includes a timestamp, so I deleted the version that includes the timestamp so I don't have to test the object against that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Arguably, we should mock timestamp out, but I think that may be out of scope for this PR. Would you mind creating a ticket and adding it to the backlog?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STENCIL-137

@splitinfinities
Copy link
Contributor Author

Stencil Site's related PR: https://github.com/ionic-team/stencil-site/pull/773/files

Comment on lines +79 to +80
buildCtx.buildResults = result.buildCtx.buildResults;
buildCtx.components = result.buildCtx.components;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why we're doing this/what's going on here - can you explain this to me please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! The components and buildResults are passed on to the buildCtx within this file from the resultant builds. These needed to be set on the buildCtx here because the promises don't update the buildCtx any more, and the original stats code needs to understand the state of the updated buildCtx after the promises all run. I wrote it this way so that we could keep the parallelization of the builds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this affect the memory usage of Stencil in a negative way? I mean, I'm sure there's some cost associated with it, but do you have an idea of the order of magnitude?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this comment still holds true, and may likely improve since I brought the parallelization back.

I was worried that adding these to the build context would increase the memory usage significantly. Luckily that is not the case. I ran 10 builds of ionic-framework using master and this PR. The increase is relatively minor:

Build # master #3030
1 MEM: 682.1MB MEM: 658.6MB
2 MEM: 743.8MB MEM: 677.3MB
3 MEM: 546.5MB MEM: 757.6MB
4 MEM: 727.2MB MEM: 697.9MB
5 MEM: 731.8MB MEM: 658.1MB
6 MEM: 730.2MB MEM: 656.2MB
7 MEM: 738.5MB MEM: 749.1MB
8 MEM: 726.2MB MEM: 655.7MB
9 MEM: 709.2MB MEM: 684.9MB
10 MEM: 663.2MB MEM: 617.7MB
Average MEM: 681.3MB MEM: 699.9MB

@rwaskiewicz rwaskiewicz merged commit c76dca7 into master Sep 24, 2021
@rwaskiewicz rwaskiewicz deleted the STENCIL-43-fix-stats-flag branch September 24, 2021 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants