Skip to content

Commit

Permalink
feat: simplify processors and split instances for each source type
Browse files Browse the repository at this point in the history
  • Loading branch information
ala-n committed Jan 24, 2024
1 parent cc08309 commit c6fceb0
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 100 deletions.
6 changes: 3 additions & 3 deletions src/core/base/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {SyntheticEventTarget} from '@exadel/esl/modules/esl-utils/dom';
import {decorate} from '@exadel/esl/modules/esl-utils/decorators';
import {microtask} from '@exadel/esl/modules/esl-utils/async';

import {UIPNormalizationService} from '../processors/normalization';
import {UIPJSNormalizationPreprocessors, UIPHTMLNormalizationPreprocessors} from '../processors/normalization';
import {UIPSnippetItem} from './snippet';

import type {UIPRoot} from './root';
Expand Down Expand Up @@ -55,7 +55,7 @@ export class UIPStateModel extends SyntheticEventTarget {
* @param modifier - plugin, that initiates the change
*/
public setJS(js: string, modifier: UIPPlugin | UIPRoot): void {
const script = UIPNormalizationService.normalize(js, false);
const script = UIPJSNormalizationPreprocessors.preprocess(js);
if (this._js === script) return;
this._js = script;
this._lastModifier = modifier;
Expand All @@ -68,7 +68,7 @@ export class UIPStateModel extends SyntheticEventTarget {
* @param modifier - plugin, that initiates the change
*/
public setHtml(markup: string, modifier: UIPPlugin | UIPRoot): void {
const html = UIPNormalizationService.normalize(markup);
const html = UIPHTMLNormalizationPreprocessors.preprocess(markup);
const root = new DOMParser().parseFromString(html, 'text/html').body;
if (!root || root.innerHTML.trim() !== this.html.trim()) {
this._html = root;
Expand Down
8 changes: 4 additions & 4 deletions src/core/preview/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {afterNextRender, skipOneRender} from '@exadel/esl/modules/esl-utils/asyn

import {UIPPlugin} from '../base/plugin';
import {UIPRenderingTemplatesService} from '../processors/templates';
import {UIPRenderingPreprocessorService} from '../processors/rendering';
import {UIPJSRenderingPreprocessors, UIPHTMLRenderingPreprocessors} from '../processors/rendering';

import type {ESLIntersectionEvent} from '@exadel/esl/modules/esl-event-listener/core';

Expand Down Expand Up @@ -76,7 +76,7 @@ export class UIPPreview extends UIPPlugin {

/** Writes the content directly to the inner area (non-isolated frame) */
protected writeContent(): void {
this.$inner.innerHTML = UIPRenderingPreprocessorService.preprocess(this.model!.html);
this.$inner.innerHTML = UIPHTMLRenderingPreprocessors.preprocess(this.model!.html);
this.stopIframeResizeLoop();
}

Expand All @@ -89,8 +89,8 @@ export class UIPPreview extends UIPPlugin {
}

const title = this.model!.activeSnippet?.label || 'UI Playground';
const script = this.model!.js;
const content = UIPRenderingPreprocessorService.preprocess(this.model!.html);
const script = UIPJSRenderingPreprocessors.preprocess(this.model!.js);
const content = UIPHTMLRenderingPreprocessors.preprocess(this.model!.html);
const html = UIPRenderingTemplatesService.render(this.isolationTemplate, {title, content, script});

this.$iframe.contentWindow?.document.open();
Expand Down
86 changes: 31 additions & 55 deletions src/core/processors/normalization.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,36 @@
/** Function to process html string for normalization*/
export type UIPNormalizationProcessor = ((input: string) => string) & {
htmlOnly?: boolean;
};
import {UIPPreprocessorService} from './preprocessor';

export class UIPNormalizationService {
/** Processor storage */
protected static processors: Record<string, UIPNormalizationProcessor> = {};
/**
* Normalization processors store for JS content.
* Normalization transformation are used to normalize content before displaying or processing.
*/
export const UIPJSNormalizationPreprocessors = new UIPPreprocessorService();
/**
* Normalization processors store for HTML content.
* Normalization transformation are used to normalize content before displaying or processing.
*/
export const UIPHTMLNormalizationPreprocessors = new UIPPreprocessorService();

/** Add processor function to normalization process */
public static addProcessor(
name: string,
processor: UIPNormalizationProcessor,
htmlOnly = false
): void {
Object.assign(processor, {htmlOnly});
UIPNormalizationService.processors[name] = processor;
}

/** Normalizes passes content string by running all registered processors in chain */
public static normalize(content: string, isHtml = true): string {
return Object.keys(UIPNormalizationService.processors)
.map((name) => UIPNormalizationService.processors[name])
.filter((processor) => typeof processor === 'function')
.filter((processor) => !processor.htmlOnly || isHtml)
.reduce((input, processor) => processor(input), content);
}
/** Removes extra indents to beautify content alignment */
export function removeIndent(input: string): string {
// Get all indents from text
const indents = input.match(/^[^\S\n\r]*(?=\S)/gm);
// No processing if no indent on the first line or input is empty
if (!indents || !indents[0].length) return input;
// Sort indents by length
indents.sort((a, b) => a.length - b.length);
// No processing if minimal indent is 0
if (!indents[0].length) return input;
// Remove indents from text
return input.replace(RegExp('^' + indents[0], 'gm'), '');
}
UIPJSNormalizationPreprocessors.add('remove-leading-indent', removeIndent);
UIPHTMLNormalizationPreprocessors.add('remove-leading-indent', removeIndent);

/** Removes extra indents */
UIPNormalizationService.addProcessor(
'remove-indent',
(input: string): string => {
// Get all indents from text
const indents = input.match(/^[^\S\n\r]*(?=\S)/gm);
// No processing if no indent on the first line or input is empty
if (!indents || !indents[0].length) return input;
// Sort indents by length
indents.sort((a, b) => a.length - b.length);
// No processing if minimal indent is 0
if (!indents[0].length) return input;
// Remove indents from text
return input.replace(RegExp('^' + indents[0], 'gm'), '');
}
);
/** Removes extra spaces */
// TODO: handle case with inline script with literal double spaces
UIPNormalizationService.addProcessor(
'remove-trailing',
(input: string) => input.replace(/\s*?$/gm, ''),
true
);
/** Remove beginning spaces */
UIPNormalizationService.addProcessor('left-trim', (input: string) =>
input.replace(/^\s+/, '')
);
/** Remove ending spaces */
UIPNormalizationService.addProcessor('right-trim', (input: string) =>
input.replace(/\s+$/, '')
);
/** Trim content */
UIPJSNormalizationPreprocessors.add('trim', (content: string) => content.trim());
UIPHTMLNormalizationPreprocessors.add('trim', (content: string) => content.trim());

/** Removes extra spaces inside the content. Applicable for HTML only */
UIPHTMLNormalizationPreprocessors.addRegexReplacer('remove-trailing', /\s*?$/gm, '');
43 changes: 43 additions & 0 deletions src/core/processors/preprocessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** Pre-processor function, called to process content */
export type UIPTransformer = (input: string) => string;

/** Rendering pre-processor service, that stores collection of {@link UIPRenderingPreprocessor}s */
export class UIPPreprocessorService {
/** Pre-processor storage */
protected preprocessors: Record<string, UIPTransformer> = {};

/** Add pre-processor {@link UIPRenderingPreprocessor} */
public add(name: string, preprocessor: UIPTransformer): void {
this.preprocessors[name] = preprocessor;
}

/** Add pre-processor alias */
public addAlias(name: string, alias: string): void {
this.preprocessors[name] = this.preprocessors[alias];
}

/** Add pre-processor with RegExp replacer */
public addRegexReplacer(name: string, regex: RegExp, replaceValue: string): void;
/** Add pre-processor with RegExp replacer */
public addRegexReplacer(name: string, regex: RegExp, replacer: (substring: string, ...args: any[]) => string): void;
public addRegexReplacer(name: string, regex: RegExp, replacer: any): void {
this.add(name, (input) => input.replace(regex, replacer));
}

public get(name: string): UIPTransformer | undefined {
return this.preprocessors[name];
}

/** Pre-process html content */
public preprocess(html: string): string {
return Object.keys(this.preprocessors)
.map((name) => this.preprocessors[name])
.filter((preprocessor) => typeof preprocessor === 'function')
.reduce((input, preprocessor) => preprocessor(input), html);
}

/** Clear all pre-processors */
public clear(): void {
this.preprocessors = {};
}
}
53 changes: 15 additions & 38 deletions src/core/processors/rendering.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,25 @@
import {ESLRandomText} from '@exadel/esl/modules/esl-random-text/core';

/** Rendering pre-processor function, called before rendering, does not affect the content in plugins */
export type UIPRenderingPreprocessor = (input: string) => string;

/** Rendering pre-processor service, that stores collection of {@link UIPRenderingPreprocessor}s */
export class UIPRenderingPreprocessorService {
protected static preprocessors: Record<string, UIPRenderingPreprocessor> = {};

/** Add pre-processor {@link UIPRenderingPreprocessor} */
public static addPreprocessor(name: string, preprocessor: UIPRenderingPreprocessor): void {
UIPRenderingPreprocessorService.preprocessors[name] = preprocessor;
}

/** Add pre-processor with RegExp replacer */
public static addRegexPreprocessor(name: string, regex: RegExp, replacer: Parameters<typeof String.prototype.replace>[1]): void {
this.addPreprocessor(name, (input) => input.replace(regex, replacer));
}

/** Add pre-processor alias */
public static addPreprocessorAlias(name: string, alias: string): void {
UIPRenderingPreprocessorService.preprocessors[name] = UIPRenderingPreprocessorService.preprocessors[alias];
}

/** Pre-process html content */
public static preprocess(html: string): string {
return Object.keys(UIPRenderingPreprocessorService.preprocessors)
.map((name) => UIPRenderingPreprocessorService.preprocessors[name])
.filter((preprocessor) => typeof preprocessor === 'function')
.reduce((input, preprocessor) => preprocessor(input), html);
}

/** Clear all pre-processors */
public static clear(): void {
UIPRenderingPreprocessorService.preprocessors = {};
}
}
import {UIPPreprocessorService} from './preprocessor';

/**
* Pre-processor services for JS content.
* Rendering preprocessors applied to content before rendering and does not affect editor's content
*/
export const UIPJSRenderingPreprocessors = new UIPPreprocessorService();
/**
* Pre-processor services for HTML content.
* Rendering preprocessors applied to content before rendering and does not affect editor's content
*/
export const UIPHTMLRenderingPreprocessors = new UIPPreprocessorService();

// Register default pre-processors
UIPRenderingPreprocessorService.addRegexPreprocessor('text', /<!--\s*text\s*x?(\d+)?\s*-->/g, (term, count) => {

UIPHTMLRenderingPreprocessors.addRegexReplacer('text', /<!--\s*text\s*x?(\d+)?\s*-->/g, (term, count) => {
const length = count ? parseInt(count, 10) : 10;
return ESLRandomText.generateText(length);
});

UIPRenderingPreprocessorService.addRegexPreprocessor('text-html', /<!--\s*(text-html|paragraph)\s*x?(\d+)?\s*-->/g, (term, name, count) => {
UIPHTMLRenderingPreprocessors.addRegexReplacer('text-html', /<!--\s*(text-html|paragraph)\s*x?(\d+)?\s*-->/g, (term, name, count) => {
const length = count ? parseInt(count, 10) : 100;
return ESLRandomText.generateTextHTML(100 * length);
});

0 comments on commit c6fceb0

Please sign in to comment.