angular/packages/compiler/src/util.ts
Paul Gschwendtner a50e2da64a fix(localize): ensure transitively loaded compiler code is tree-shakable (#45405)
The localize primary entry-point (used at runtime in application code)
indirectly loads from the compiler package for computing message ids.
The compiler package has a couple of constants which cannot be DCE-ded/
tree-shaken due to side-effect reliance that is detected by Terser.

We fix these constants to be three-shakable. Note that another issue
technically would be that the compiler package has a side-effect call
for `publishFacade` (for JIT), but that invocation is marked as pure by
the Angular CLI babel optimization pipeline. So this results is no
unused code currently but is risky and should be addressed in the future.

PR Close #45405
2022-04-21 11:09:39 -07:00

175 lines
5.2 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
export function dashCaseToCamelCase(input: string): string {
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
}
export function splitAtColon(input: string, defaultValues: string[]): string[] {
return _splitAt(input, ':', defaultValues);
}
export function splitAtPeriod(input: string, defaultValues: string[]): string[] {
return _splitAt(input, '.', defaultValues);
}
function _splitAt(input: string, character: string, defaultValues: string[]): string[] {
const characterIndex = input.indexOf(character);
if (characterIndex == -1) return defaultValues;
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
}
export function noUndefined<T>(val: T|undefined): T {
return val === undefined ? null! : val;
}
export function error(msg: string): never {
throw new Error(`Internal Error: ${msg}`);
}
// Escape characters that have a special meaning in Regular Expressions
export function escapeRegExp(s: string): string {
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
export type Byte = number;
export function utf8Encode(str: string): Byte[] {
let encoded: Byte[] = [];
for (let index = 0; index < str.length; index++) {
let codePoint = str.charCodeAt(index);
// decode surrogate
// see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
const low = str.charCodeAt(index + 1);
if (low >= 0xdc00 && low <= 0xdfff) {
index++;
codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
}
}
if (codePoint <= 0x7f) {
encoded.push(codePoint);
} else if (codePoint <= 0x7ff) {
encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
} else if (codePoint <= 0xffff) {
encoded.push(
(codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
} else if (codePoint <= 0x1fffff) {
encoded.push(
((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80,
((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
}
}
return encoded;
}
export function stringify(token: any): string {
if (typeof token === 'string') {
return token;
}
if (Array.isArray(token)) {
return '[' + token.map(stringify).join(', ') + ']';
}
if (token == null) {
return '' + token;
}
if (token.overriddenName) {
return `${token.overriddenName}`;
}
if (token.name) {
return `${token.name}`;
}
if (!token.toString) {
return 'object';
}
// WARNING: do not try to `JSON.stringify(token)` here
// see https://github.com/angular/angular/issues/23440
const res = token.toString();
if (res == null) {
return '' + res;
}
const newLineIndex = res.indexOf('\n');
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
}
export class Version {
public readonly major: string;
public readonly minor: string;
public readonly patch: string;
constructor(public full: string) {
const splits = full.split('.');
this.major = splits[0];
this.minor = splits[1];
this.patch = splits.slice(2).join('.');
}
}
export interface Console {
log(message: string): void;
warn(message: string): void;
}
declare var WorkerGlobalScope: any;
// CommonJS / Node have global context exposed as "global" variable.
// We don't want to include the whole node.d.ts this this compilation unit so we'll just fake
// the global "global" var for now.
declare var global: any;
// Check `global` first, because in Node tests both `global` and `window` may be defined and our
// `_global` variable should point to the NodeJS `global` in that case. Note: Typeof/Instanceof
// checks are considered side-effects in Terser. We explicitly mark this as side-effect free:
// https://github.com/terser/terser/issues/250.
const _global: {[name: string]: any} = (/* @__PURE__ */ (
() => (typeof global !== 'undefined' && global) || (typeof window !== 'undefined' && window) ||
(typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
self instanceof WorkerGlobalScope && self))());
export {_global as global};
export function newArray<T = any>(size: number): T[];
export function newArray<T>(size: number, value: T): T[];
export function newArray<T>(size: number, value?: T): T[] {
const list: T[] = [];
for (let i = 0; i < size; i++) {
list.push(value!);
}
return list;
}
/**
* Partitions a given array into 2 arrays, based on a boolean value returned by the condition
* function.
*
* @param arr Input array that should be partitioned
* @param conditionFn Condition function that is called for each item in a given array and returns a
* boolean value.
*/
export function partitionArray<T, F = T>(
arr: (T|F)[], conditionFn: (value: T|F) => boolean): [T[], F[]] {
const truthy: T[] = [];
const falsy: F[] = [];
for (const item of arr) {
(conditionFn(item) ? truthy : falsy).push(item as any);
}
return [truthy, falsy];
}