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
Add integration test for side effects, ban top-level property access #29329
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
"master": { | ||
"uncompressed": { | ||
"runtime": 1497, | ||
"main": 164945, | ||
"main": 166739, | ||
"polyfills": 43626 | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# The check-side-effects package generates and deletes this file. | ||
# If the process is killed, it will be left behind. | ||
check-side-effects.tmp-input.js |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
This test checks if the side effects for loading Angular packages have changed using <https://github.com/filipesilva/check-side-effects>. | ||
|
||
Running `yarn test` will check all ES modules listed in `side-effects.json`. | ||
|
||
Running `yarn update` will update any changed side effects. | ||
|
||
To add a new ES module to this test, add a new entry in `side-effects.json`. | ||
|
||
Usually the ESM and FESM should have the same output, but retained objects that were renamed during the flattening step will leave behind a different name. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "angular-side-effects", | ||
"version": "0.0.0", | ||
"license": "MIT", | ||
"scripts": { | ||
"test": "check-side-effects --test side-effects.json --pure-getters", | ||
"update": "yarn test --update" | ||
}, | ||
"dependencies": { | ||
"@angular/animations": "file:../../dist/packages-dist/animations", | ||
"@angular/common": "file:../../dist/packages-dist/common", | ||
"@angular/core": "file:../../dist/packages-dist/core", | ||
"@angular/elements": "file:../../dist/packages-dist/elements", | ||
"@angular/forms": "file:../../dist/packages-dist/forms", | ||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", | ||
"@angular/router": "file:../../dist/packages-dist/router", | ||
"check-side-effects": "file:../../node_modules/check-side-effects" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
{ | ||
"tests": [ | ||
{ | ||
"esModules": "./node_modules/@angular/animations/esm5/animations.js", | ||
"expectedOutput": "./snapshots/animations/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/fesm5/animations.js", | ||
"expectedOutput": "./snapshots/animations/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/esm2015/animations.js", | ||
"expectedOutput": "./snapshots/animations/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/fesm2015/animations.js", | ||
"expectedOutput": "./snapshots/animations/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/esm5/browser/browser.js", | ||
"expectedOutput": "./snapshots/animations-browser/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/fesm5/browser.js", | ||
"expectedOutput": "./snapshots/animations-browser/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/esm2015/browser/browser.js", | ||
"expectedOutput": "./snapshots/animations-browser/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/animations/fesm2015/browser.js", | ||
"expectedOutput": "./snapshots/animations-browser/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/common/esm5/common.js", | ||
"expectedOutput": "./snapshots/common/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/common/fesm5/common.js", | ||
"expectedOutput": "./snapshots/common/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/common/esm2015/common.js", | ||
"expectedOutput": "./snapshots/common/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/common/fesm2015/common.js", | ||
"expectedOutput": "./snapshots/common/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/core/esm5/core.js", | ||
"expectedOutput": "./snapshots/core/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/core/fesm5/core.js", | ||
"expectedOutput": "./snapshots/core/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/core/esm2015/core.js", | ||
"expectedOutput": "./snapshots/core/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/core/fesm2015/core.js", | ||
"expectedOutput": "./snapshots/core/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/elements/esm5/elements.js", | ||
"expectedOutput": "./snapshots/elements/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/elements/fesm5/elements.js", | ||
"expectedOutput": "./snapshots/elements/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/elements/esm2015/elements.js", | ||
"expectedOutput": "./snapshots/elements/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/elements/fesm2015/elements.js", | ||
"expectedOutput": "./snapshots/elements/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/forms/esm5/forms.js", | ||
"expectedOutput": "./snapshots/forms/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/forms/fesm5/forms.js", | ||
"expectedOutput": "./snapshots/forms/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/forms/esm2015/forms.js", | ||
"expectedOutput": "./snapshots/forms/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/forms/fesm2015/forms.js", | ||
"expectedOutput": "./snapshots/forms/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/platform-browser/esm5/platform-browser.js", | ||
"expectedOutput": "./snapshots/platform-browser/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/platform-browser/fesm5/platform-browser.js", | ||
"expectedOutput": "./snapshots/platform-browser/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/platform-browser/esm2015/platform-browser.js", | ||
"expectedOutput": "./snapshots/platform-browser/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/platform-browser/fesm2015/platform-browser.js", | ||
"expectedOutput": "./snapshots/platform-browser/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/router/esm5/router.js", | ||
"expectedOutput": "./snapshots/router/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/router/fesm5/router.js", | ||
"expectedOutput": "./snapshots/router/esm5.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/router/esm2015/router.js", | ||
"expectedOutput": "./snapshots/router/esm2015.js" | ||
}, | ||
{ | ||
"esModules": "./node_modules/@angular/router/fesm2015/router.js", | ||
"expectedOutput": "./snapshots/router/esm2015.js" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import "@angular/animations"; | ||
|
||
import "@angular/core"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import "tslib"; | ||
|
||
import "@angular/animations"; | ||
|
||
import "@angular/core"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import "@angular/core"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import "@angular/core"; | ||
|
||
import "tslib"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { Subject, Subscription } from "rxjs"; | ||
|
||
import "rxjs/operators"; | ||
|
||
function getGlobal() { | ||
const __globalThis = "undefined" !== typeof globalThis && globalThis; | ||
const __window = "undefined" !== typeof window && window; | ||
const __self = "undefined" !== typeof self && "undefined" !== typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && self; | ||
const __global = "undefined" !== typeof global && global; | ||
return __globalThis || __global || __window || __self; | ||
} | ||
|
||
const _global = getGlobal(); | ||
|
||
let _symbolIterator = null; | ||
|
||
function getSymbolIterator() { | ||
if (!_symbolIterator) { | ||
const Symbol = _global["Symbol"]; | ||
if (Symbol && Symbol.iterator) _symbolIterator = Symbol.iterator; else { | ||
const keys = Object.getOwnPropertyNames(Map.prototype); | ||
for (let i = 0; i < keys.length; ++i) { | ||
const key = keys[i]; | ||
if ("entries" !== key && "size" !== key && Map.prototype[key] === Map.prototype["entries"]) _symbolIterator = key; | ||
} | ||
} | ||
} | ||
return _symbolIterator; | ||
} | ||
|
||
if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg; | ||
|
||
function flatten(list, mapFn) { | ||
const result = []; | ||
let i = 0; | ||
while (i < list.length) { | ||
const item = list[i]; | ||
if (Array.isArray(item)) if (item.length > 0) { | ||
list = item.concat(list.slice(i + 1)); | ||
i = 0; | ||
} else i++; else { | ||
result.push(mapFn ? mapFn(item) : item); | ||
i++; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
class EventEmitter extends Subject { | ||
constructor(isAsync = false) { | ||
super(); | ||
this.__isAsync = isAsync; | ||
} | ||
emit(value) { | ||
super.next(value); | ||
} | ||
subscribe(generatorOrNext, error, complete) { | ||
let schedulerFn; | ||
let errorFn = err => null; | ||
let completeFn = () => null; | ||
if (generatorOrNext && "object" === typeof generatorOrNext) { | ||
schedulerFn = this.__isAsync ? value => { | ||
setTimeout(() => generatorOrNext.next(value)); | ||
} : value => { | ||
generatorOrNext.next(value); | ||
}; | ||
if (generatorOrNext.error) errorFn = this.__isAsync ? err => { | ||
setTimeout(() => generatorOrNext.error(err)); | ||
} : err => { | ||
generatorOrNext.error(err); | ||
}; | ||
if (generatorOrNext.complete) completeFn = this.__isAsync ? () => { | ||
setTimeout(() => generatorOrNext.complete()); | ||
} : () => { | ||
generatorOrNext.complete(); | ||
}; | ||
} else { | ||
schedulerFn = this.__isAsync ? value => { | ||
setTimeout(() => generatorOrNext(value)); | ||
} : value => { | ||
generatorOrNext(value); | ||
}; | ||
if (error) errorFn = this.__isAsync ? err => { | ||
setTimeout(() => error(err)); | ||
} : err => { | ||
error(err); | ||
}; | ||
if (complete) completeFn = this.__isAsync ? () => { | ||
setTimeout(() => complete()); | ||
} : () => { | ||
complete(); | ||
}; | ||
} | ||
const sink = super.subscribe(schedulerFn, errorFn, completeFn); | ||
if (generatorOrNext instanceof Subscription) generatorOrNext.add(sink); | ||
return sink; | ||
} | ||
} | ||
|
||
class QueryList { | ||
constructor() { | ||
this.dirty = true; | ||
this._results = []; | ||
this.changes = new EventEmitter(); | ||
this.length = 0; | ||
} | ||
map(fn) { | ||
return this._results.map(fn); | ||
} | ||
filter(fn) { | ||
return this._results.filter(fn); | ||
} | ||
find(fn) { | ||
return this._results.find(fn); | ||
} | ||
reduce(fn, init) { | ||
return this._results.reduce(fn, init); | ||
} | ||
forEach(fn) { | ||
this._results.forEach(fn); | ||
} | ||
some(fn) { | ||
return this._results.some(fn); | ||
} | ||
toArray() { | ||
return this._results.slice(); | ||
} | ||
[getSymbolIterator()]() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This computed method name seems to be causing all the retained code here. Without it, the result is just
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The iterator symbol should probably just be polyfilled like any other polyfill (from core-js or manually) |
||
return this._results[getSymbolIterator()](); | ||
} | ||
toString() { | ||
return this._results.toString(); | ||
} | ||
reset(resultsTree) { | ||
this._results = flatten(resultsTree); | ||
this.dirty = false; | ||
this.length = this._results.length; | ||
this.last = this._results[this.length - 1]; | ||
this.first = this._results[0]; | ||
} | ||
notifyOnChanges() { | ||
this.changes.emit(this); | ||
} | ||
setDirty() { | ||
this.dirty = true; | ||
} | ||
destroy() { | ||
this.changes.complete(); | ||
this.changes.unsubscribe(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import "tslib"; | ||
|
||
import "rxjs"; | ||
|
||
import "rxjs/operators"; | ||
|
||
function getGlobal() { | ||
var __globalThis = "undefined" !== typeof globalThis && globalThis; | ||
var __window = "undefined" !== typeof window && window; | ||
var __self = "undefined" !== typeof self && "undefined" !== typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && self; | ||
var __global = "undefined" !== typeof global && global; | ||
return __globalThis || __global || __window || __self; | ||
} | ||
|
||
var _global = getGlobal(); | ||
|
||
if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what causes this to be retained is the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import "@angular/core"; | ||
|
||
import "rxjs"; | ||
|
||
import "rxjs/operators"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import "tslib"; | ||
|
||
import "@angular/core"; | ||
|
||
import "rxjs"; | ||
|
||
import "rxjs/operators"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import "@angular/core"; | ||
|
||
import "@angular/platform-browser"; | ||
|
||
import "rxjs"; | ||
|
||
import "rxjs/operators"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import "tslib"; | ||
|
||
import "@angular/core"; | ||
|
||
import "@angular/platform-browser"; | ||
|
||
import "rxjs"; | ||
|
||
import "rxjs/operators"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@filipesilva why this size regression?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this regression is due to IIFEs being more code, especially when transpiled to ES5. Most of those IIFEs remain because their variable is actually used in a default CLI app.
For instance
versus
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried to validate this by making some before/after diffs with prettified output. They are pretty noisy because vars change name.
But you can see it here: filipesilva/compare-toplevel-prop-iife@3f116e9#diff-7a9076d6d94e62c13d641aa71f19ae8eL4109
Before:
After
Another example in filipesilva/compare-toplevel-prop-iife@3f116e9#diff-7a9076d6d94e62c13d641aa71f19ae8eL1558
I eyeballed the whole diff and couldn't see anything else out of place. A couple of blocks moved because their var name changed but that was it.
I also found what the concrete before and after numbers are though. It is now 166,558 before and 166,741 after.
So I think what's really happening is that this PR isn't increasing the size by more than 1%. Rather, the payload limited is listed as 164,945, which allows for values between -1% (163,295) and +1%(166,594). But the current size was already almost breaking the +1% threshold, and the 183 Bytes just pushed it over the edge.