@angular/core#isDevMode TypeScript Examples
The following examples show how to use
@angular/core#isDevMode.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: hooksReplacer.ts From ngx-dynamic-hooks with MIT License | 6 votes |
/**
* Outputs a warning in the console when the positions of two hooks are invalid in some manner
*
* @param message - The error message
* @param hookPos - The first HookPosition
* @param prevHookPos - The second HookPosition
* @param content - The source text for the HookPositions
*/
private generateHookPosWarning(message: string, hookPos: HookPosition, prevHookPos: HookPosition, content: string): void {
if (isDevMode()) {
const prevHookSegments = this.getHookSegments(prevHookPos, content);
const hookSegments = this.getHookSegments(hookPos, content);
const prevHookData = {
openingTag: prevHookSegments.openingTag,
openingTagStartIndex: prevHookPos.openingTagStartIndex,
openingTagEndIndex: prevHookPos.openingTagEndIndex,
closingTag: prevHookSegments.closingTag,
closingTagStartIndex: prevHookPos.closingTagStartIndex,
closingTagEndIndex: prevHookPos.closingTagEndIndex
};
const hookData = {
openingTag: hookSegments.openingTag,
openingTagStartIndex: hookPos.openingTagStartIndex,
openingTagEndIndex: hookPos.openingTagEndIndex,
closingTag: hookSegments.closingTag,
closingTagStartIndex: hookPos.closingTagStartIndex,
closingTagEndIndex: hookPos.closingTagEndIndex
};
console.warn(message + '\nFirst hook: ', prevHookData, '\nSecond hook:', hookData);
}
}
Example #2
Source File: app.component.ts From SideQuest with MIT License | 6 votes |
constructor(
private spinnerService: LoadingSpinnerService,
public statusService: StatusBarService,
private adbService: AdbClientService,
public appService: AppService,
public webService: WebviewService,
public dragService: DragAndDropService,
private electronService: ElectronService
) {
this.devMode = isDevMode();
}
Example #3
Source File: thorchain-network.service.ts From thorchain-explorer-singlechain with MIT License | 6 votes |
setNetwork(network: THORChainNetwork) {
const proxy = 'https://a2wva4alb6.execute-api.us-east-1.amazonaws.com/dev/thornode';
switch (network) {
case THORChainNetwork.TESTNET:
this.midgardBasePath = 'https://midgard.bepswap.com';
this.nodeBasePath = (isDevMode()) ? 'http://44.235.130.167:1317' : proxy;
this.network = network;
this.nodeReqParams = new HttpParams().set('network', THORChainNetwork.TESTNET);
break;
/**
* Right now default is CHAOSNET
*/
default:
this.midgardBasePath = 'https://chaosnet-midgard.bepswap.com';
this.nodeBasePath = (isDevMode()) ? 'http://18.159.173.48:1317' : proxy;
this.network = network;
this.nodeReqParams = new HttpParams().set('network', THORChainNetwork.CHAOSNET);
break;
}
this._networkUpdated.next(network);
}
Example #4
Source File: deepComparer.ts From ngx-dynamic-hooks with MIT License | 6 votes |
/**
* Tests if two objects are equal by value
*
* @param a - The first object
* @param b - The second object
* @param compareDepth - How many levels deep to compare
*/
isEqual(a, b, compareDepth?: number): boolean {
const aStringified = this.detailedStringify(a, compareDepth);
const bStringified = this.detailedStringify(b, compareDepth);
if (aStringified.result === null || bStringified.result === null) {
if (isDevMode) {
console.warn(
'Objects could not be compared by value as one or both of them could not be stringified. Returning false. \n',
'Objects:', a, b
);
}
return false;
}
return aStringified.result === bStringified.result;
}
Example #5
Source File: i18n.service.ts From alauda-ui with MIT License | 6 votes |
translate(key: string, data?: StringMap, ignoreNonExist = false) {
let content = this._i18n.translation[key];
if (content == null) {
if (isDevMode() && !ignoreNonExist) {
console.warn(`No exist translate key for ${key}`);
}
return key;
}
if (data) {
content = content.replaceAll(
/{{([^{}]+)}}/,
(_matched, $0: string) => data[$0.trim()],
);
}
return content;
}
Example #6
Source File: dataTypeParser.ts From ngx-dynamic-hooks with MIT License | 6 votes |
/**
* A safe wrapper around the loadContextVariable function. Returns undefined if there is any error.
*
* @param contextVar - The context var
* @param context - The context object
* @param event - An event object, if available
* @param unescapeStrings - Whether to unescape strings or not
* @param trackContextVariables - An optional object that will be filled out with all found context vars
* @param allowContextFunctionCalls - Whether function calls in context vars are allowed
*/
safelyLoadContextVariable(contextVar: string, context: any = {}, event?: any, unescapeStrings: boolean = true, trackContextVariables: any = {}, allowContextFunctionCalls: boolean = true): any {
try {
const resolvedContextVariable = this.loadContextVariable(contextVar, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls);
trackContextVariables[this.decodeDataTypeString(contextVar)] = resolvedContextVariable;
return resolvedContextVariable;
} catch (e) {
if (isDevMode()) {
console.warn(e);
}
trackContextVariables[this.decodeDataTypeString(contextVar)] = undefined;
return undefined;
}
}
Example #7
Source File: image-crop.component.ts From ASW-Form-Builder with MIT License | 6 votes |
private activatePinchGesture(): void {
if (this.Hammer) {
const hammer = new this.Hammer(this.wrapper.nativeElement);
hammer.get('pinch').set({ enable: true });
hammer.on('pinchmove', this.onPinch.bind(this));
hammer.on('pinchend', this.pinchStop.bind(this));
hammer.on('pinchstart', this.startPinch.bind(this));
} else if (isDevMode()) {
console.warn('[AswImageCropper] Could not find HammerJS - Pinch Gesture won\'t work');
}
}
Example #8
Source File: content.service.ts From open-source with MIT License | 6 votes |
compile(markdown: string, decodeHtml = false): string {
const trimmed = this.trimIndentation(markdown);
const decoded = decodeHtml ? this.decodeHtml(trimmed) : trimmed;
const compiled = marked(decoded, {
renderer: new marked.Renderer(),
breaks: false,
gfm: true,
headerIds: true,
silent: !isDevMode(),
smartLists: true,
smartypants: true,
xhtml: true,
});
return this.sanitizer.sanitize(SecurityContext.HTML, compiled) || '';
}
Example #9
Source File: log-driver.service.ts From open-source with MIT License | 6 votes |
log(event: DynLog): any {
// do not log anything on production
// or below the configured limit
if (!isDevMode() || !(event.level & this.level)) {
return;
}
return this.getLogger(event.level)(event);
}
Example #10
Source File: database.service.ts From client-side-databases with Apache License 2.0 | 6 votes |
async function loadRxDBPlugins(): Promise<any> {
addRxPlugin(RxDBLocalDocumentsPlugin);
addRxPlugin(RxDBLeaderElectionPlugin);
// enable replication via graphql
addRxPlugin(RxDBReplicationGraphQLPlugin);
/**
* key-compression
*/
addRxPlugin(RxDBKeyCompressionPlugin);
/**
* to reduce the build-size,
* we use some modules in dev-mode only
*/
if (isDevMode()) {
await Promise.all([
/**
* Enable the dev mode plugin
*/
import('rxdb/plugins/dev-mode').then(
module => addRxPlugin(module.RxDBDevModePlugin)
),
// we use the schema-validation only in dev-mode
// this validates each document if it is matching the jsonschema
import('rxdb/plugins/validate').then(
module => addRxPlugin(module.RxDBValidatePlugin)
)
]);
} else {
// in production we do not use any validation plugin
// to reduce the build-size
}
}
Example #11
Source File: parserEntryResolver.ts From ngx-dynamic-hooks with MIT License | 6 votes |
/**
* A black/whitelist validation function for the benefit of the user. Outputs warnings in the console if something is off.
*
* @param parsers - The parsers in question
* @param blacklist - The blacklist in question
* @param whitelist - The whitelist in question
*/
checkBlackAndWhitelist(parsers: Array<HookParser>, blacklist?: Array<string>, whitelist?: Array<string>): void {
const parserNames = parsers.map(entry => entry.name).filter(entry => entry !== undefined);
if (blacklist) {
for (const blacklistedParser of blacklist) {
if (!parserNames.includes(blacklistedParser)) {
if (isDevMode()) {
console.warn('Blacklisted parser name "' + blacklistedParser + '" does not appear in the list of global parsers names. Make sure both spellings are identical.');
}
}
}
}
if (whitelist) {
for (const whitelistedParser of whitelist) {
if (!parserNames.includes(whitelistedParser)) {
if (isDevMode()) {
console.warn('Whitelisted parser name "' + whitelistedParser + '" does not appear in the list of global parsers names. Make sure both spellings are identical.');
}
}
}
}
}
Example #12
Source File: parserEntryResolver.ts From ngx-dynamic-hooks with MIT License | 6 votes |
/**
* Makes sure that all parser names are unique
*
* @param parsers - The parsers in question
*/
checkParserNames(parsers: Array<HookParser>): void {
const parserNames = parsers.map(entry => entry.name).filter(entry => entry !== undefined);
const previousNames = [];
const alreadyWarnedNames = [];
for (const parserName of parserNames) {
if (previousNames.includes(parserName) && !alreadyWarnedNames.includes(parserName)) {
if (isDevMode()) {
console.warn('Parser name "' + parserName + '" is not unique and appears multiple times in the list of active parsers.');
alreadyWarnedNames.push(parserName);
}
}
previousNames.push(parserName);
}
}
Example #13
Source File: database.service.ts From client-side-databases with Apache License 2.0 | 6 votes |
async function loadRxDBPlugins(): Promise<any> {
addRxPlugin(RxDBLocalDocumentsPlugin);
addRxPlugin(RxDBLeaderElectionPlugin);
// enable replication via graphql
addRxPlugin(RxDBReplicationGraphQLPlugin);
/**
* key-compression
*/
addRxPlugin(RxDBKeyCompressionPlugin);
/**
* to reduce the build-size,
* we use some modules in dev-mode only
*/
if (isDevMode()) {
await Promise.all([
/**
* Enable the dev mode plugin
*/
import('rxdb/plugins/dev-mode').then(
module => addRxPlugin(module.RxDBDevModePlugin)
),
// we use the schema-validation only in dev-mode
// this validates each document if it is matching the jsonschema
import('rxdb/plugins/validate').then(
module => addRxPlugin(module.RxDBValidatePlugin)
)
]);
} else {
// in production we do not use any validation plugin
// to reduce the build-size
}
}
Example #14
Source File: app.component.ts From rubic-app with GNU General Public License v3.0 | 6 votes |
/**
* Log current dev build timestamp.
* @private
*/
private printTimestamp(): void {
if (isDevMode()) {
// @ts-ignore
import('@app/timestamp')
.then(data => console.debug(`It's a development build, timestamp: ${data.timestamp}`))
.catch(() => {
console.debug('timestamp file is not found');
});
}
}
Example #15
Source File: dev-mode.service.ts From ngx-tippy-wrapper with MIT License | 5 votes |
@Injectable({ providedIn: 'root' })
export class DevModeService implements isDevMode {
public isDevMode() {
return isDevMode();
}
}
Example #16
Source File: database.service.ts From client-side-databases with Apache License 2.0 | 5 votes |
async function loadRxDBPlugins(): Promise<any> {
addRxPlugin(RxDBLocalDocumentsPlugin);
addRxPlugin(RxDBLeaderElectionPlugin);
// enable replication via graphql
addRxPlugin(RxDBReplicationGraphQLPlugin);
/**
* indexed-db adapter
*/
addPouchPlugin(PouchdbAdapterIdb);
/**
* key-compression
*/
addRxPlugin(RxDBKeyCompressionPlugin);
/**
* to reduce the build-size,
* we use some modules in dev-mode only
*/
if (isDevMode()) {
await Promise.all([
/**
* Enable the dev mode plugin
*/
import('rxdb/plugins/dev-mode').then(
module => addRxPlugin(module.RxDBDevModePlugin)
),
// we use the schema-validation only in dev-mode
// this validates each document if it is matching the jsonschema
import('rxdb/plugins/validate').then(
module => addRxPlugin(module.RxDBValidatePlugin)
),
// enable debug to detect slow queries
import('pouchdb-debug' + '').then(
module => addPouchPlugin(module['default'])
)
]);
PouchDB.debug.enable('pouchdb:find');
} else {
// in production we do not use any validation plugin
// to reduce the build-size
}
}
Example #17
Source File: dev-mode.service.ts From ngx-tippy-wrapper with MIT License | 5 votes |
public isDevMode() {
return isDevMode();
}
Example #18
Source File: webview.service.ts From SideQuest with MIT License | 5 votes |
setupWebview() {
let customCss = `
::-webkit-scrollbar {
height: 16px;
width: 16px;
background: #e0e0e0;
}
::-webkit-scrollbar-corner {
background: #cfcfcf;
}
::-webkit-scrollbar-thumb {
background: #ed4e7a;
}
.-sidequest{
background-image: url('https://i.imgur.com/Hy2oclv.png');
background-size: 12.814px 15px;
}
.bsaber-tooltip.-sidequest::after {
content: "Add to SideQuest";
}
.-sidequest-remove{
background-image: url('https://i.imgur.com/5ZPR9L1.png');
background-size: 15px 15px;
}
.bsaber-tooltip.-sidequest-remove::after {
content: "Remove Custom Level";
}`;
this.webView.addEventListener('did-start-loading', e => {
this.isWebviewLoading = true;
this.webView.insertCSS(customCss);
});
this.webView.addEventListener('did-navigate-in-page', e => {
this.currentAddress = this.webView.getURL();
});
this.webView.addEventListener('did-stop-loading', async e => {
this.currentAddress = this.webView.getURL();
this.isWebviewLoading = false;
this.webView.insertCSS(customCss);
this.isLoaded();
if (isDevMode()) {
this.webView.openDevTools();
}
});
}
Example #19
Source File: bindingStateManager.ts From ngx-dynamic-hooks with MIT License | 5 votes |
/**
* Takes a standard hook opening tag and parses Angular-style inputs from it
*
* @param openingTag - The hook opening tag to parse
* @param parserConfig - The current parser config
*/
private createOutputBindings(openingTag: string, parserConfig: SelectorHookParserConfig): {[key: string]: RichBindingData} {
const rawOutputs = this.getBindingsFromOpeningTag('outputs', openingTag, parserConfig.outputsBlacklist, parserConfig.outputsWhitelist);
// Create bindings object
const outputBindings: {[key: string]: RichBindingData} = {};
for (const [rawOutputKey, rawOutputValue] of Object.entries(rawOutputs)) {
outputBindings[rawOutputKey] = {
raw: rawOutputValue,
value: rawOutputValue,
boundContextVariables: {}
};
}
// Create output callback functions
for (const [outputName, outputBinding] of Object.entries(outputBindings)) {
// Simply wrap evaluate() into outer function that will be called when the corresponding event emits.
// If the dataTypeString contains a function, it will therefore be evaluated and executed "just-in-time".
// If it contains a variable, nothing will happen as the the evaluated dataTypeString is not assigned anywhere and simply discarded.
outputBindings[outputName].value = (event, context) => {
try {
this.dataTypeParser.evaluate(
outputBinding.raw,
parserConfig.allowContextInBindings ? context : {},
event,
parserConfig.unescapeStrings,
outputBinding.boundContextVariables,
parserConfig.allowContextFunctionCalls
);
} catch (e) {
if (isDevMode()) {
e.message = `Hook output parsing error\nselector: ` + parserConfig.selector + `\noutput: ` + outputName + `\nvalue: "` + outputBinding.raw + `"\nmessage: "` + e.message + `"`;
console.error(e);
}
}
};
}
return outputBindings;
}
Example #20
Source File: bindingStateManager.ts From ngx-dynamic-hooks with MIT License | 5 votes |
/**
* Creates the input bindings object on the first evaluation of a hook
*
* @param openingTag - The opening tag to analyze
* @param context - The current context object
* @param parserConfig - The current parser config
*/
private createInputBindings(openingTag: string, context: any, parserConfig: SelectorHookParserConfig): {[key: string]: RichBindingData} {
// Read inputs from opening tag
const rawNoBracketInputs = this.getBindingsFromOpeningTag('noBracketInputs', openingTag, parserConfig.inputsBlacklist, parserConfig.inputsWhitelist);
const rawBracketInputs = this.getBindingsFromOpeningTag('bracketInputs', openingTag, parserConfig.inputsBlacklist, parserConfig.inputsWhitelist);
// NoBracketInputs are to be interpreted as plain strings, so wrap them in quotes
for (const [noBracketInputName, noBracketInputValue] of Object.entries(rawNoBracketInputs)) {
rawNoBracketInputs[noBracketInputName] = "'" + noBracketInputValue + "'";
}
// Merge both input objects
const mergedRawInputs = {...rawNoBracketInputs, ...rawBracketInputs};
// Create bindings object
const inputBindings: {[key: string]: RichBindingData} = {};
for (const [rawInputKey, rawInputValue] of Object.entries(mergedRawInputs)) {
inputBindings[rawInputKey] = {
raw: rawInputValue,
value: rawInputValue,
boundContextVariables: {}
};
}
// If no need to parse, return result immediately
if (!parserConfig.parseInputs) {
return inputBindings;
}
// Parse input strings and track all bound context variables
for (const [inputName, inputBinding] of Object.entries(inputBindings)) {
try {
inputBindings[inputName].value = this.dataTypeParser.evaluate(
inputBinding.raw,
parserConfig.allowContextInBindings ? context : {},
undefined,
parserConfig.unescapeStrings,
inputBindings[inputName].boundContextVariables,
parserConfig.allowContextFunctionCalls
);
} catch (e) {
if (isDevMode()) {
e.message = `Hook input parsing error\nselector: ` + parserConfig.selector + `\ninput: ` + inputName + `\nvalue: "` + inputBinding.value + `"\nmessage: "` + e.message + `"`;
console.error(e);
}
// If binding could not be parsed at all due to syntax error, remove from list of inputs.
// No amount of calls to updateInputBindings() will fix this kind of error.
delete inputBindings[inputName];
}
}
return inputBindings;
}
Example #21
Source File: componentUpdater.ts From ngx-dynamic-hooks with MIT License | 5 votes |
/**
* Checks whether two detailedStringifiedResults can be compared and throws lots of errors and warnings if not
*
* @param bindingName - The binding in question
* @param componentName - The component in question
* @param compareDepth - The current compareDepth
* @param oldResult - The detailedStringifiedResult for the old value
* @param newResult - The detailedStringifiedResult for the new value
*/
checkDetailedStringifyResultPair(bindingName: string, componentName: string, compareDepth: number, oldResult: DetailedStringifyResult, newResult: DetailedStringifyResult): boolean {
// Stringify successful?
if (oldResult.result === null && newResult.result === null) {
if (isDevMode()) {
console.warn('Could stringify neither new nor old value for hook binding "' + bindingName + '" for component "' + componentName + '" to compare by value. Defaulting to comparison by reference instead.');
return false;
}
}
if (oldResult.result === null) {
if (isDevMode()) {
console.warn('Could not stringify old value for hook binding "' + bindingName + '" for component "' + componentName + '" to compare by value. Defaulting to comparison by reference instead.');
return false;
}
}
if (newResult.result === null) {
if (isDevMode()) {
console.warn('Could not stringify new value for hook binding "' + bindingName + '" for component "' + componentName + '" to compare by value. Defaulting to comparison by reference instead.');
return false;
}
}
// Max depth reached?
if (oldResult.depthReachedCount > 0 && newResult.depthReachedCount > 0) {
if (isDevMode()) {
console.warn(
'Maximum compareByValueDepth of ' + compareDepth + ' reached ' + newResult.depthReachedCount + ' time(s) for new value and ' + oldResult.depthReachedCount + ' time(s) for old value while comparing binding "' + bindingName + '" for component "' + componentName + '.\n',
'If this impacts performance, consider simplifying this binding, reducing comparison depth or setting compareInputsByValue/compareOutputsByValue to false.'
);
}
} else if (oldResult.depthReachedCount > 0) {
if (isDevMode()) {
console.warn(
'Maximum compareByValueDepth of ' + compareDepth + ' reached ' + oldResult.depthReachedCount + ' time(s) for old value while comparing binding "' + bindingName + '" for component "' + componentName + '.\n',
'If this impacts performance, consider simplifying this binding, reducing comparison depth or setting compareInputsByValue/compareOutputsByValue to false.',
);
}
} else if (newResult.depthReachedCount > 0) {
if (isDevMode()) {
console.warn(
'Maximum compareByValueDepth of ' + compareDepth + ' reached ' + newResult.depthReachedCount + ' time(s) for new value while comparing binding "' + bindingName + '" for component "' + componentName + '.\n',
'If this impacts performance, consider simplifying this binding, reducing comparison depth or setting compareInputsByValue/compareOutputsByValue to false.',
);
}
}
return true;
}
Example #22
Source File: componentCreator.ts From ngx-dynamic-hooks with MIT License | 5 votes |
// Component creation
// ----------------------------------------------------------------------------------------------------------------
/**
* Takes a hook along with a DOM node and loads the specified component class (normal or lazy-loaded).
* Returns a subject the emits the component class when ready.
*
* @param componentConfig - The componentConfig from HookData
*/
loadComponentClass(componentConfig: ComponentConfig): ReplaySubject<new(...args: any[]) => any> {
const componentClassLoaded: ReplaySubject<new(...args: any[]) => any> = new ReplaySubject(1);
// a) If is normal class
if (componentConfig.hasOwnProperty('prototype')) {
componentClassLoaded.next(componentConfig as (new(...args: any[]) => any));
// b) If is LazyLoadComponentConfig
} else if (componentConfig.hasOwnProperty('importPromise') && componentConfig.hasOwnProperty('importName')) {
// Catch typical importPromise error
if ((componentConfig as LazyLoadComponentConfig).importPromise instanceof Promise) {
throw Error(`When lazy-loading a component, the "importPromise"-field must contain a function returning the import-promise, but it contained the promise itself.`);
}
// Warning if using old Angular version
const ngVersion = this.platform.getNgVersion();
if (ngVersion < 9 && isDevMode()) {
console.warn('It seems you are trying to use lazy-loaded-components with an Angular version older than 9. Please note that this functionality requires the new Ivy renderer to be enabled.');
}
(componentConfig as LazyLoadComponentConfig).importPromise().then((m) => {
const importName = (componentConfig as LazyLoadComponentConfig).importName;
const compClass = m.hasOwnProperty(importName) ? m[importName] : m['default'];
componentClassLoaded.next(compClass);
});
} else {
throw Error('The "component" property of a returned HookData object must either contain the component class or a LazyLoadComponentConfig');
}
return componentClassLoaded;
}
Example #23
Source File: parserEntryResolver.ts From ngx-dynamic-hooks with MIT License | 5 votes |
/**
* Figures out what kind of config object (out of all possible types) the HookParserEntry is and loads it appropriately.
*
* The potential types are:
* - a service
* - a class
* - an instance
* - an object literal to configure SelectorHookParser with
*
* @param parserEntry - The HookParserEntry to process
* @param injector - The injector to use for resolving this parser
*/
resolveEntry(parserEntry: HookParserEntry, injector: Injector): HookParser {
// Check if class
if (parserEntry.hasOwnProperty('prototype')) {
// Check if service
try {
return injector.get(parserEntry);
// Otherwise instantiate manually
} catch (e) {
return new (parserEntry as new(...args: any[]) => any)();
}
}
// Check if object
else if (typeof parserEntry === 'object') {
// Is instance
if (parserEntry.constructor.name !== 'Object') {
return parserEntry as HookParser;
// Is object literal
} else {
try {
return new SelectorHookParser(parserEntry as SelectorHookParserConfig, this.parserResolver, this.selectorFinder, this.bindingStateManager);
} catch (e) {
if (isDevMode()) {
console.error('Invalid parser config - ', parserEntry, '\n', e);
}
}
}
} else {
if (isDevMode()) {
console.error('Invalid parser config - ', parserEntry);
}
}
}
Example #24
Source File: user.service.ts From ng-conf-2020-workshop with MIT License | 5 votes |
/** Save new login as current user */
public setCurrentUser(user: User) {
if (isDevMode()) {
this.localStorage.setItem(USER_TOKEN, JSON.stringify(user));
}
this._currentUser = user;
}
Example #25
Source File: hookFinder.ts From ngx-dynamic-hooks with MIT License | 4 votes |
/**
* Finds all hooks that are enclosing in a string of text, e.g. <hook>...</hook>
*
* Correctly finding enclosing hooks requires a programmatic parser rather then just regex alone, as regex cannot handle
* patterns that are potentially nested within themselves.
*
* - If the content between the opening and closing is lazy (.*?), it would take the first closing tag after the opening tag,
* regardless if it belongs to the opening tag or actually a nested hook. This would falsely match the first and third tag
* in this example: '<hook><hook></hook></hook>'
*
* - If the content between the opening and closing is greedy (.*), it would only end on the last closing tag in the string,
* ignoring any previous closing tags. This would falsely match the first and fourth tag in this example:
* '<hook></hook><hook></hook>'
*
* There is no regex that works for both scenarios. This method therefore manually counts and compares the opening tags with the closing tags.
*
* @param content - The text to parse
* @param openingTagRegex - The regex for the opening tag
* @param closingTagRegex - The regex for the closing tag
* @param includeNested - Whether to include nested hooks in the result
*/
findEnclosingHooks(content: string, openingTagRegex: RegExp, closingTagRegex: RegExp, includeNested: boolean = true): Array<HookPosition> {
const allTags = [];
const result: Array<HookPosition> = [];
// Find all opening tags
const openingTagMatches = matchAll(content, openingTagRegex);
for (const match of openingTagMatches) {
allTags.push({
isOpening: true,
value: match[0],
startIndex: match.index,
endIndex: match.index + match[0].length
});
}
// Find all closing tags
const closingTagMatches = matchAll(content, closingTagRegex);
for (const match of closingTagMatches) {
allTags.push({
isOpening: false,
value: match[0],
startIndex: match.index,
endIndex: match.index + match[0].length
});
}
// Sort by startIndex
allTags.sort((a, b) => a.startIndex - b.startIndex);
// Create HookPositions by figuring out which opening tag belongs to which closing tag
const openedTags = [];
allTagsLoop: for (const [index, tag] of allTags.entries()) {
// Any subsequent tag is only allowed to start after previous tag has ended
if (index > 0 && tag.startIndex < allTags[index - 1].endIndex) {
if (isDevMode()) {
console.warn('Syntax error - New tag "' + tag.value + '" started at position ' + tag.startIndex + ' before previous tag "' + allTags[index - 1].value + '" ended at position ' + allTags[index - 1].endIndex + '. Ignoring.');
}
continue;
}
// Opening or closing tag?
if (tag.isOpening) {
openedTags.push(tag);
} else {
// Syntax error: Closing tag without preceding opening tag. Syntax error.
if (openedTags.length === 0) {
if (isDevMode()) {
console.warn('Syntax error - Closing tag without preceding opening tag found: "' + tag.value + '". Ignoring.');
}
continue;
}
// If nested hooks not allowed and more than one tag is open, discard both this closing tag and the latest opening tag
if (!includeNested && openedTags.length > 1) {
openedTags.pop();
continue;
}
// Valid hook! Add to result array
const openingTag = openedTags[openedTags.length - 1];
result.push({
openingTagStartIndex: openingTag.startIndex,
openingTagEndIndex: openingTag.startIndex + openingTag.value.length,
closingTagStartIndex: tag.startIndex,
closingTagEndIndex: tag.startIndex + tag.value.length
});
openedTags.pop();
}
}
if (openedTags.length > 0) {
if (isDevMode()) {
console.warn('Syntax error - Opening tags without corresponding closing tags found.');
}
}
return result;
}
Example #26
Source File: dataTypeParser.ts From ngx-dynamic-hooks with MIT License | 4 votes |
/**
* Takes a context variable string and evaluates it to get the desired value
*
* IMPORTANT: To correctly parse variables, their substrings, subfunction and subbrackets must be encoded (done in evaluate())
*
* @param contextVar - The context var
* @param context - The context object
* @param event - An event object, if available
* @param unescapeStrings - Whether to unescape strings or not
* @param trackContextVariables - An optional object that will be filled out with all found context vars
* @param allowContextFunctionCalls - Whether function calls in context vars are allowed
*/
loadContextVariable(contextVar: string, context: any = {}, event?: any, unescapeStrings: boolean = true, trackContextVariables: any = {}, allowContextFunctionCalls: boolean = true): any {
const shortContextVar = contextVar.substr(7); // Cut off 'context' from the front
// If context object is requested directly
if (shortContextVar.trim() === '') {
return context;
}
// Otherwise, create variable path array and fetch value, so the context object can be easily travelled.
// Variable path example: 'restaurants["newOrleans"].reviews[5]' becomes ['restaurants', 'newOrleans', 'reviews', 5],
const path = [];
const pathMatches = matchAll(shortContextVar, new RegExp(regexes.variablePathPartRegex, 'gm'));
for (const match of pathMatches) {
// 1. If dot notation
if (match[0].startsWith('.')) {
path.push({
type: 'property',
value: match[0].substr(1)
});
}
// 2. If bracket notation
if (match[0].startsWith('[') && match[0].endsWith(']')) {
let bracketValue = match[0].substr(1, match[0].length - 2);
// Evaluate bracket parameter
bracketValue = this.decodeDataTypeString(bracketValue); // Decode variable
bracketValue = this.evaluate(bracketValue, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls); // Recursively repeat the process
path.push({
type: 'property',
value: bracketValue
});
}
// 3. If function call
if (match[0].startsWith('(') && match[0].endsWith(')')) {
// Check if function calls are allowed
if (!allowContextFunctionCalls) {
throw Error('Tried to call a function in a context variable. This has been disallowed in the current config.');
}
const funcParams = match[0].substr(1, match[0].length - 2); // Strip outer brackets
// Evaluate function parameters
const paramsArray = [];
if (funcParams !== '') {
for (const param of funcParams.split(',')) {
let p = this.decodeDataTypeString(param); // Decode variable
p = this.evaluate(p, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls); // Recursively repeat the process
paramsArray.push(p);
}
}
// Add function to path
path.push({
type: 'function',
value: paramsArray
});
}
}
try {
return this.fetchContextVariable(context, JSON.parse(JSON.stringify(path)));
} catch (e) {
if (isDevMode()) {
console.warn(e);
}
throw Error('The required context variable "' + this.decodeDataTypeString(contextVar) + '" could not be found in the context object. Returning undefined instead.');
}
}
Example #27
Source File: hooksReplacer.ts From ngx-dynamic-hooks with MIT License | 4 votes |
/**
* Checks the hookPositions of the combined parserResults to ensure they do not collide/overlap.
* Any that do are removed from the results.
*
* @param parserResults - The parserResults from replaceHooksWithNodes()
* @param content - The source text for the parserResults
*/
private validateHookPositions(parserResults: Array<ParserResult>, content: string): Array<ParserResult> {
const checkedParserResults = [];
outerloop: for (const [index, parserResult] of parserResults.entries()) {
const enclosing = (Number.isInteger(parserResult.hookPosition.closingTagStartIndex) && Number.isInteger(parserResult.hookPosition.closingTagEndIndex));
const hookPos = parserResult.hookPosition;
// Check if hook is in itself well-formed
if (hookPos.openingTagStartIndex >= hookPos.openingTagEndIndex) {
if (isDevMode()) { console.warn('Error when checking hook positions - openingTagEndIndex has to be greater than openingTagStartIndex. Ignoring.', hookPos); }
continue;
}
if (enclosing && hookPos.openingTagEndIndex > hookPos.closingTagStartIndex) {
if (isDevMode()) { console.warn('Error when checking hook positions - The closing tag must start after the opening tag has concluded. Ignoring.', hookPos); }
continue;
}
if (enclosing && hookPos.closingTagStartIndex >= hookPos.closingTagEndIndex) {
if (isDevMode()) { console.warn('Error when checking hook positions - closingTagEndIndex has to be greater than closingTagStartIndex. Ignoring.', hookPos); }
continue;
}
// Check if hook overlaps with other hooks
const previousHooks = parserResults.slice(0, index);
innerloop: for (const previousHook of previousHooks) {
const prevHookPos = previousHook.hookPosition;
const prevIsEnclosing = (Number.isInteger(prevHookPos.closingTagStartIndex) && Number.isInteger(prevHookPos.closingTagEndIndex));
// Check if identical hook position
if (
hookPos.openingTagStartIndex === prevHookPos.openingTagStartIndex &&
hookPos.openingTagEndIndex === prevHookPos.openingTagEndIndex &&
(!enclosing || !prevIsEnclosing || (
hookPos.closingTagStartIndex === prevHookPos.closingTagStartIndex &&
hookPos.closingTagEndIndex === prevHookPos.closingTagEndIndex
))
) {
this.generateHookPosWarning('A hook with the same position as another hook was found. There may be multiple identical parsers active that are looking for the same hook. Ignoring duplicates.', hookPos, prevHookPos, content);
continue outerloop;
}
// Opening tag must begin after previous opening tag has ended
if (hookPos.openingTagStartIndex < prevHookPos.openingTagEndIndex) {
this.generateHookPosWarning('Error when checking hook positions: Hook opening tag starts before previous hook opening tag ends. Ignoring.', hookPos, prevHookPos, content);
continue outerloop;
}
// Opening tag must not overlap with previous closing tag
if (prevIsEnclosing && !(
hookPos.openingTagEndIndex <= prevHookPos.closingTagStartIndex ||
hookPos.openingTagStartIndex >= prevHookPos.closingTagEndIndex
)) {
this.generateHookPosWarning('Error when checking hook positions: Opening tag of hook overlaps with closing tag of previous hook. Ignoring.', hookPos, prevHookPos, content);
continue outerloop;
}
// Closing tag must not overlap with previous closing tag
if (prevIsEnclosing && enclosing && !(
hookPos.closingTagEndIndex <= prevHookPos.closingTagStartIndex ||
hookPos.closingTagStartIndex >= prevHookPos.closingTagEndIndex
)) {
this.generateHookPosWarning('Error when checking hook positions: Closing tag of hook overlaps with closing tag of previous hook. Ignoring.', hookPos, prevHookPos, content);
continue outerloop;
}
// Check if hooks are incorrectly nested, e.g. "<outer-hook><inner-hook></outer-hook></inner-hook>"
if (enclosing && prevIsEnclosing &&
hookPos.openingTagEndIndex <= prevHookPos.closingTagStartIndex &&
hookPos.closingTagStartIndex >= prevHookPos.closingTagEndIndex
) {
this.generateHookPosWarning('Error when checking hook positions: The closing tag of a nested hook lies beyond the closing tag of the outer hook. Ignoring.', hookPos, prevHookPos, content);
continue outerloop;
}
}
// If everything okay, add to result array
checkedParserResults.push(parserResult);
}
return checkedParserResults;
}
Example #28
Source File: componentCreator.ts From ngx-dynamic-hooks with MIT License | 4 votes |
/**
* The main entry function to start the dynamic component initialization process
*
* @param contentElement - The container element with the component selector tags in its innerHTML
* @param hookIndex - The current hookIndex (ids must match component selector ids)
* @param token - The token used for parsetoken-attribute of the component selectors
* @param context - The current context object
* @param options - The current HookComponentOptions
* @param injector - The injector to use for the dynamically-create components
*/
init(contentElement: any, hookIndex: HookIndex, token: string, context: any, options: OutletOptions, injector: Injector): ReplaySubject<boolean> {
const allComponentsLoaded: ReplaySubject<boolean> = new ReplaySubject(1);
const componentLoadSubjects = [];
const hookHostElements = {};
// Get HookData, replace placeholders and create desired content slots
for (const [hookId, hook] of Object.entries(hookIndex)) {
const placeholderElement = this.platform.findPlaceholderElement(contentElement, token, hookId);
if (placeholderElement) {
hook.data = hook.parser.loadComponent(hook.id, hook.value, context, this.platform.getChildNodes(placeholderElement));
// Replace placeholder element with component selector element (or a generic anchor element if lazy-loaded)
hookHostElements[hookId] = this.replacePlaceholderElement(placeholderElement, hook);
// Also replace child nodes with all desired ng-content slots
// Note: Doing this immediately after the evaluation of each hook so that succeeding hooks that are
// removed by the previous' hooks ng-content don't even need to be evaluated (let alone loaded below)
this.createContentSlotElements(hookHostElements[hookId], hook, token);
} else {
// If removed by previous hook in loop via ng-content replacement
delete hookIndex[hook.id];
}
}
// Load all components from hookIndex into the prepared host elements
for (const [hookId, hook] of Object.entries(hookIndex)) {
// Start with of(true) to catch errors from loadComponentClass in the observable stream as well
componentLoadSubjects.push(of(true)
// Load component class first (might be lazy-loaded)
.pipe(mergeMap(() => this.loadComponentClass(hook.data.component)))
.pipe(tap((compClass) => {
// Check if the host element is simply an anchor for a lazily-loaded component. If so, insert proper selector now.
hookHostElements[hookId] = this.handleAnchorElement(hookHostElements[hookId], compClass);
// Get projectableNodes from the content slots
const projectableNodes = this.getContentSlotElements(hookHostElements[hookId], token);
// Instantiate component
this.createComponent(hook, context, hookHostElements[hookId], projectableNodes, options, compClass, injector);
}))
// If could not be created, remove from hookIndex
.pipe(catchError((e) => {
if (isDevMode()) {
console.error(e.message);
}
delete hookIndex[hook.id];
return of(null);
})));
}
// If no components in text, no need to progress further
if (componentLoadSubjects.length === 0) {
allComponentsLoaded.next(true);
return allComponentsLoaded;
}
// Once all normal and lazy components have loaded
combineLatest([...componentLoadSubjects]).pipe(first()).subscribe(() => {
// Call dynamic lifecycle methods for all created components
for (const hook of Object.values(hookIndex)) {
// Find all content children components
const contentChildren: Array<DynamicContentChild> = [];
if (typeof hook.componentRef.instance['onDynamicMount'] === 'function' || typeof hook.componentRef.instance['onDynamicChanges'] === 'function') {
this.findContentChildren(hook.componentRef.location.nativeElement, contentChildren, hookIndex, token);
}
// OnDynamicChanges
if (typeof hook.componentRef.instance['onDynamicChanges'] === 'function') {
hook.componentRef.instance['onDynamicChanges']({contentChildren});
}
// OnDynamicMount
if (typeof hook.componentRef.instance['onDynamicMount'] === 'function') {
hook.componentRef.instance['onDynamicMount']({context, contentChildren});
}
}
// Remove now redundant attributes from component elements
for (const hostElement of Object.values(hookHostElements)) {
this.renderer.removeAttribute(hostElement, 'hookid');
this.renderer.removeAttribute(hostElement, 'parsetoken');
this.renderer.removeAttribute(hostElement, 'parser');
this.renderer.removeAttribute(hostElement, 'ng-version');
}
// Done!
allComponentsLoaded.next(true);
});
return allComponentsLoaded;
}