Skip to content

Commit

Permalink
refactor(core): make the error messages tree shakable (#44359)
Browse files Browse the repository at this point in the history
Long error messages can be tree-shaken in the production build and replaced with error codes.

See: #44219 (comment)

PR Close #44359
  • Loading branch information
ramthir authored and dylhunn committed Jan 19, 2022
1 parent 929788d commit 3797d10
Show file tree
Hide file tree
Showing 21 changed files with 146 additions and 46 deletions.
26 changes: 25 additions & 1 deletion goldens/public-api/core/errors.md
Expand Up @@ -31,10 +31,28 @@ export const enum RuntimeErrorCode {
// (undocumented)
EXPRESSION_CHANGED_AFTER_CHECKED = -100,
// (undocumented)
INJECTOR_ALREADY_DESTROYED = 205,
// (undocumented)
INVALID_DIFFER_INPUT = 900,
// (undocumented)
INVALID_EVENT_BINDING = 306,
// (undocumented)
INVALID_FACTORY_DEPENDENCY = 202,
// (undocumented)
INVALID_I18N_STRUCTURE = 700,
// (undocumented)
INVALID_INHERITANCE = 903,
// (undocumented)
INVALID_INJECTION_TOKEN = 204,
// (undocumented)
MISSING_INJECTION_CONTEXT = 203,
// (undocumented)
MULTIPLE_COMPONENTS_MATCH = -300,
// (undocumented)
MULTIPLE_PLATFORMS = 400,
// (undocumented)
NO_SUPPORTING_DIFFER_FACTORY = 901,
// (undocumented)
PIPE_NOT_FOUND = -302,
// (undocumented)
PLATFORM_NOT_FOUND = 401,
Expand All @@ -47,7 +65,13 @@ export const enum RuntimeErrorCode {
// (undocumented)
UNKNOWN_BINDING = 303,
// (undocumented)
UNKNOWN_ELEMENT = 304
UNKNOWN_ELEMENT = 304,
// (undocumented)
UNSAFE_VALUE_IN_RESOURCE_URL = 904,
// (undocumented)
UNSAFE_VALUE_IN_SCRIPT = 905,
// (undocumented)
VIEW_ALREADY_ATTACHED = 902
}

// (No @packageDocumentation comment for this package)
Expand Down
2 changes: 1 addition & 1 deletion goldens/size-tracking/aio-payloads.json
Expand Up @@ -15,7 +15,7 @@
"master": {
"uncompressed": {
"runtime": 4330,
"main": 458518,
"main": 457719,
"polyfills": 37271,
"styles": 69863,
"light-theme": 77232,
Expand Down
18 changes: 9 additions & 9 deletions goldens/size-tracking/integration-payloads.json
Expand Up @@ -3,8 +3,8 @@
"master": {
"uncompressed": {
"runtime": 1083,
"main": 140067,
"polyfills": 36964
"main": 139416,
"polyfills": 37226
}
}
},
Expand All @@ -24,7 +24,7 @@
"master": {
"uncompressed": {
"runtime": 1105,
"main": 145783,
"main": 145132,
"polyfills": 37248
}
}
Expand All @@ -33,27 +33,27 @@
"master": {
"uncompressed": {
"runtime": 929,
"main": 138435,
"main": 137712,
"polyfills": 37933
}
}
},
"cli-hello-world-lazy": {
"master": {
"uncompressed": {
"runtime": 2812,
"main": 232617,
"runtime": 2835,
"main": 231989,
"polyfills": 37244,
"src_app_lazy_lazy_module_ts": 796
"src_app_lazy_lazy_module_ts": 795
}
}
},
"forms": {
"master": {
"uncompressed": {
"runtime": 1063,
"main": 162811,
"polyfills": 36975
"main": 162121,
"polyfills": 37206
}
}
},
Expand Down
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {stringify} from '../../util/stringify';
import {isListLikeIterable, iterateListLike} from '../change_detection_util';

Expand Down Expand Up @@ -152,8 +153,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
diff(collection: NgIterable<V>|null|undefined): DefaultIterableDiffer<V>|null {
if (collection == null) collection = [];
if (!isListLikeIterable(collection)) {
throw new Error(
`Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed` :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_DIFFER_INPUT, errorMessage);
}

if (this.check(collection)) {
Expand Down
Expand Up @@ -6,8 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/

import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {stringify} from '../../util/stringify';
import {isJsObject} from '../change_detection_util';

import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs';


Expand Down Expand Up @@ -79,8 +81,10 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
if (!map) {
map = new Map();
} else if (!(map instanceof Map || isJsObject(map))) {
throw new Error(
`Error trying to diff '${stringify(map)}'. Only maps and objects are allowed`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Error trying to diff '${stringify(map)}'. Only maps and objects are allowed` :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_DIFFER_INPUT, errorMessage);
}

return this.check(map) ? this : null;
Expand Down
Expand Up @@ -9,6 +9,7 @@
import {ɵɵdefineInjectable} from '../../di/interface/defs';
import {StaticProvider} from '../../di/interface/provider';
import {Optional, SkipSelf} from '../../di/metadata';
import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ';


Expand Down Expand Up @@ -250,8 +251,11 @@ export class IterableDiffers {
if (factory != null) {
return factory;
} else {
throw new Error(`Cannot find a differ supporting object '${iterable}' of type '${
getTypeNameForDebugging(iterable)}'`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Cannot find a differ supporting object '${iterable}' of type '${
getTypeNameForDebugging(iterable)}'` :
'';
throw new RuntimeError(RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY, errorMessage);
}
}
}
Expand Down
Expand Up @@ -7,6 +7,8 @@
*/

import {Optional, SkipSelf, StaticProvider, ɵɵdefineInjectable} from '../../di';
import {RuntimeError, RuntimeErrorCode} from '../../errors';

import {DefaultKeyValueDifferFactory} from './default_keyvalue_differ';


Expand Down Expand Up @@ -181,6 +183,9 @@ export class KeyValueDiffers {
if (factory) {
return factory;
}
throw new Error(`Cannot find a differ supporting object '${kv}'`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Cannot find a differ supporting object '${kv}'` :
'';
throw new RuntimeError(RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY, errorMessage);
}
}
11 changes: 9 additions & 2 deletions packages/core/src/di/injector_compatibility.ts
Expand Up @@ -8,6 +8,7 @@

import '../util/ng_dev_mode';

import {RuntimeError, RuntimeErrorCode} from '../errors';
import {Type} from '../interface/type';
import {getClosureSafeProperty} from '../util/property';
import {stringify} from '../util/stringify';
Expand Down Expand Up @@ -58,7 +59,10 @@ export function injectInjectorOnly<T>(token: ProviderToken<T>, flags?: InjectFla
export function injectInjectorOnly<T>(token: ProviderToken<T>, flags = InjectFlags.Default): T|
null {
if (_currentInjector === undefined) {
throw new Error(`inject() must be called from an injection context`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`inject() must be called from an injection context` :
'';
throw new RuntimeError(RuntimeErrorCode.MISSING_INJECTION_CONTEXT, errorMessage);
} else if (_currentInjector === null) {
return injectRootLimpMode(token, undefined, flags);
} else {
Expand Down Expand Up @@ -141,7 +145,10 @@ export function injectArgs(types: (ProviderToken<any>|any[])[]): any[] {
const arg = resolveForwardRef(types[i]);
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new Error('Arguments array must have arguments.');
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
'Arguments array must have arguments.' :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_DIFFER_INPUT, errorMessage);
}
let type: Type<any>|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
Expand Down
19 changes: 15 additions & 4 deletions packages/core/src/di/r3_injector.ts
Expand Up @@ -8,6 +8,7 @@

import '../util/ng_dev_mode';

import {RuntimeError, RuntimeErrorCode} from '../errors';
import {OnDestroy} from '../interface/lifecycle_hooks';
import {Type} from '../interface/type';
import {FactoryFn, getFactoryDef} from '../render3/definition_factory';
Expand Down Expand Up @@ -255,7 +256,10 @@ export class R3Injector {

private assertNotDestroyed(): void {
if (this._destroyed) {
throw new Error('Injector has already been destroyed.');
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
'Injector has already been destroyed.' :
'';
throw new RuntimeError(RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED, errorMessage);
}
}

Expand Down Expand Up @@ -446,7 +450,10 @@ function injectableDefOrInjectorDefFactory(token: ProviderToken<any>): FactoryFn
// InjectionTokens should have an injectable def (ɵprov) and thus should be handled above.
// If it's missing that, it's an error.
if (token instanceof InjectionToken) {
throw new Error(`Token ${stringify(token)} is missing a ɵprov definition.`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Token ${stringify(token)} is missing a ɵprov definition.` :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_INJECTION_TOKEN, errorMessage);
}

// Undecorated types can sometimes be created if they have no constructor arguments.
Expand All @@ -455,15 +462,19 @@ function injectableDefOrInjectorDefFactory(token: ProviderToken<any>): FactoryFn
}

// There was no way to resolve a factory for this token.
throw new Error('unreachable');
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ? 'unreachable' : '';
throw new RuntimeError(RuntimeErrorCode.INVALID_INJECTION_TOKEN, errorMessage);
}

function getUndecoratedInjectableFactory(token: Function) {
// If the token has parameters then it has dependencies that we cannot resolve implicitly.
const paramLength = token.length;
if (paramLength > 0) {
const args: string[] = newArray(paramLength, '?');
throw new Error(`Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
`Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).` :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_INJECTION_TOKEN, errorMessage);
}

// The constructor function appears to have no parameters.
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/errors.ts
Expand Up @@ -26,6 +26,10 @@ export const enum RuntimeErrorCode {
// Dependency Injection Errors
CYCLIC_DI_DEPENDENCY = -200,
PROVIDER_NOT_FOUND = -201,
INVALID_FACTORY_DEPENDENCY = 202,
MISSING_INJECTION_CONTEXT = 203,
INVALID_INJECTION_TOKEN = 204,
INJECTOR_ALREADY_DESTROYED = 205,

// Template Errors
MULTIPLE_COMPONENTS_MATCH = -300,
Expand All @@ -34,6 +38,7 @@ export const enum RuntimeErrorCode {
UNKNOWN_BINDING = 303,
UNKNOWN_ELEMENT = 304,
TEMPLATE_STRUCTURE_ERROR = 305,
INVALID_EVENT_BINDING = 306,

// Bootstrap Errors
MULTIPLE_PLATFORMS = 400,
Expand All @@ -48,8 +53,16 @@ export const enum RuntimeErrorCode {
// Declarations Errors

// i18n Errors
INVALID_I18N_STRUCTURE = 700,

// JIT Compilation Errors
// Other
INVALID_DIFFER_INPUT = 900,
NO_SUPPORTING_DIFFER_FACTORY = 901,
VIEW_ALREADY_ATTACHED = 902,
INVALID_INHERITANCE = 903,
UNSAFE_VALUE_IN_RESOURCE_URL = 904,
UNSAFE_VALUE_IN_SCRIPT = 905
}

export class RuntimeError<T = RuntimeErrorCode> extends Error {
Expand Down
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {Type, Writable} from '../../interface/type';
import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty';
import {fillProperties} from '../../util/property';
Expand Down Expand Up @@ -39,7 +40,10 @@ export function ɵɵInheritDefinitionFeature(definition: DirectiveDef<any>|Compo
superDef = superType.ɵcmp || superType.ɵdir;
} else {
if (superType.ɵcmp) {
throw new Error('Directives cannot inherit Components');
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
'Directives cannot inherit Components' :
'';
throw new RuntimeError(RuntimeErrorCode.INVALID_INHERITANCE, errorMessage);
}
// Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
superDef = superType.ɵdir;
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/render3/i18n/i18n_apply.ts
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {getPluralCase} from '../../i18n/localization';
import {assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertIndexInRange, throwError} from '../../util/assert';
import {assertIndexInExpandoRange, assertTIcu} from '../assert';
Expand All @@ -20,6 +21,7 @@ import {createCommentNode, createElementNode, createTextNode, nativeInsertBefore
import {getBindingIndex} from '../state';
import {renderStringify} from '../util/stringify_utils';
import {getNativeByIndex, unwrapRNode} from '../util/view_utils';

import {getLocaleId} from './i18n_locale_id';
import {getCurrentICUCaseIndex, getParentFromIcuCreateOpCode, getRefFromIcuCreateOpCode, getTIcu} from './i18n_util';

Expand Down Expand Up @@ -198,7 +200,11 @@ export function applyMutableOpCodes(
attrValue, null);
break;
default:
throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`);
if (ngDevMode) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_I18N_STRUCTURE,
`Unable to determine the type of mutate operation for "${opCode}"`);
}
}
} else {
switch (opCode) {
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/render3/view_ref.ts
Expand Up @@ -7,9 +7,11 @@
*/

import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef, ViewRefTracker} from '../linker/view_ref';
import {removeFromArray} from '../util/array_utils';
import {assertEqual} from '../util/assert';

import {collectNativeNodes} from './collect_native_nodes';
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared';
import {CONTAINER_HEADER_OFFSET, VIEW_REFS} from './interfaces/container';
Expand Down Expand Up @@ -284,7 +286,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int

attachToViewContainerRef() {
if (this._appRef) {
throw new Error('This view is already attached directly to the ApplicationRef!');
const errorMessage =
ngDevMode ? 'This view is already attached directly to the ApplicationRef!' : '';
throw new RuntimeError(RuntimeErrorCode.VIEW_ALREADY_ATTACHED, errorMessage);
}
this._attachedToViewContainer = true;
}
Expand All @@ -296,7 +300,8 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int

attachToAppRef(appRef: ViewRefTracker) {
if (this._attachedToViewContainer) {
throw new Error('This view is already attached to a ViewContainer!');
const errorMessage = ngDevMode ? 'This view is already attached to a ViewContainer!' : '';
throw new RuntimeError(RuntimeErrorCode.VIEW_ALREADY_ATTACHED, errorMessage);
}
this._appRef = appRef;
}
Expand Down

0 comments on commit 3797d10

Please sign in to comment.