Skip to content

Commit

Permalink
pull upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
Zedwag committed Oct 28, 2024
2 parents ddd2963 + db3faea commit 8565253
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 63 deletions.
20 changes: 19 additions & 1 deletion packages/devextreme-angular/src/ui/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {

import { Store } from 'devextreme/data';
import DataSource, { Options as DataSourceOptions } from 'devextreme/data/data_source';
import { ChatError, DisposingEvent, InitializedEvent, Message, MessageSendEvent, OptionChangedEvent, User } from 'devextreme/ui/chat';
import { ChatError, DisposingEvent, InitializedEvent, Message, MessageSendEvent, OptionChangedEvent, TypingEndEvent, TypingStartEvent, User } from 'devextreme/ui/chat';

import DxChat from 'devextreme/ui/chat';

Expand Down Expand Up @@ -299,6 +299,22 @@ export class DxChatComponent extends DxComponent implements OnDestroy, OnChanges
*/
@Output() onOptionChanged: EventEmitter<OptionChangedEvent>;

/**
* [descr:dxChatOptions.onTypingEnd]
*/
@Output() onTypingEnd: EventEmitter<TypingEndEvent>;

/**
* [descr:dxChatOptions.onTypingStart]
*/
@Output() onTypingStart: EventEmitter<TypingStartEvent>;

/**
* This member supports the internal infrastructure and is not intended to be used directly from your code.
Expand Down Expand Up @@ -463,6 +479,8 @@ export class DxChatComponent extends DxComponent implements OnDestroy, OnChanges
{ subscribe: 'initialized', emit: 'onInitialized' },
{ subscribe: 'messageSend', emit: 'onMessageSend' },
{ subscribe: 'optionChanged', emit: 'onOptionChanged' },
{ subscribe: 'typingEnd', emit: 'onTypingEnd' },
{ subscribe: 'typingStart', emit: 'onTypingStart' },
{ emit: 'accessKeyChange' },
{ emit: 'activeStateEnabledChange' },
{ emit: 'dataSourceChange' },
Expand Down
6 changes: 4 additions & 2 deletions packages/devextreme-react/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import dxChat, {
import { Component as BaseComponent, IHtmlOptions, ComponentRef, NestedComponentMeta } from "./core/component";
import NestedOption from "./core/nested-option";

import type { Message, DisposingEvent, InitializedEvent, MessageSendEvent, User as ChatUser } from "devextreme/ui/chat";
import type { Message, DisposingEvent, InitializedEvent, MessageSendEvent, TypingEndEvent, TypingStartEvent, User as ChatUser } from "devextreme/ui/chat";

type ReplaceFieldTypes<TSource, TReplacement> = {
[P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P];
Expand All @@ -18,6 +18,8 @@ type IChatOptionsNarrowedEvents = {
onDisposing?: ((e: DisposingEvent) => void);
onInitialized?: ((e: InitializedEvent) => void);
onMessageSend?: ((e: MessageSendEvent) => void);
onTypingEnd?: ((e: TypingEndEvent) => void);
onTypingStart?: ((e: TypingStartEvent) => void);
}

type IChatOptions = React.PropsWithChildren<ReplaceFieldTypes<Properties, IChatOptionsNarrowedEvents> & IHtmlOptions & {
Expand All @@ -43,7 +45,7 @@ const Chat = memo(
), [baseRef.current]);

const subscribableOptions = useMemo(() => (["items"]), []);
const independentEvents = useMemo(() => (["onDisposing","onInitialized","onMessageSend"]), []);
const independentEvents = useMemo(() => (["onDisposing","onInitialized","onMessageSend","onTypingEnd","onTypingStart"]), []);

const defaults = useMemo(() => ({
defaultItems: "items",
Expand Down
2 changes: 1 addition & 1 deletion packages/devextreme-themebuilder/src/metadata/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class MetadataCollector {
static getStringFromObject(
object: ThemesMetadata | string[] | FlatStylesDependencies,
): string {
return JSON.stringify(object).replace(/"/g, '\'').replace(/'(ON|OFF)'/g, '"$1"');
return JSON.stringify(object, null, 2).replace(/"/g, '\'').replace(/'(ON|OFF)'/g, '"$1"');
}

async getFileList(dirName: string): Promise<string[]> {
Expand Down
125 changes: 81 additions & 44 deletions packages/devextreme-themebuilder/src/metadata/dependency-collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import precinct from 'precinct';
import WidgetsHandler from '../modules/widgets-handler';

export const filePathMap = new Map();
const stylesRegex = /\sSTYLE (.*)/;

const busyCache = {
widget: '',
dependencies: {},
};

const REGEXP_IS_TS_NOT_DTS = /^(?!.*\.d\.ts$).*\.tsx?$/;
const REGEXP_IS_TSX_NOT_DTS = /^(?!.*\.d\.ts$).*\.tsx$/;
const REGEXP_IS_DTS = /\.d\.ts$/;

export default class DependencyCollector {
Expand All @@ -25,9 +25,11 @@ export default class DependencyCollector {
themes = ['generic', 'material'];

static getWidgetFromAst(ast: SyntaxTree): string {
const stylesRegex = /\sSTYLE (.*)/;

if (ast.comments?.length) {
const styleComment = ast.comments
.find((comment: AstComment): boolean => comment.value.includes('STYLE'));
.find((comment: AstComment): boolean => comment.value.includes('STYLE'));

if (styleComment) {
return stylesRegex.exec(styleComment.value)[1].toLowerCase();
Expand All @@ -37,34 +39,43 @@ export default class DependencyCollector {
return '';
}

static getUniqueWidgets(widgetsArray: string[], currentWidget?: string): string[] {
const fullArray = currentWidget ? [...widgetsArray, currentWidget] : widgetsArray;
static getWidgetFromSource(filePath: string): string {
const stylesRegex = /^\s*\/\/\s?STYLE (.*)/m;
const fileContent = readFileSync(filePath, 'utf8');

return [...new Set(fullArray)];
return stylesRegex.exec(fileContent)?.[1].toLowerCase() || '';
}

static getUniqueWidgets(widgetsArray: string[]): string[] {
return [...new Set(widgetsArray)];
}

static isArraysEqual(array1: string[], array2: string[]): boolean {
return array1.length === array2.length
&& array1.every((value, index) => value === array2[index]);
&& array1.every((value, index) => value === array2[index]);
}

treeProcessor(node: ScriptsDependencyTree): string[] {
treeProcessor(node: ScriptsDependencyTree, cache: Map<string, any> = new Map() ): string[] {
let result: string[] = [];
const { widget, dependencies } = node;

if (this.flatStylesDependencyTree[widget] !== undefined) {
const cachedWidgets = this.flatStylesDependencyTree[widget];
return DependencyCollector.getUniqueWidgets(cachedWidgets, widget);
}
Object.entries(dependencies).forEach(([path, nextNode]) => {
const cached = cache.get(path);

Object.values(dependencies).forEach((nextNode) => {
result.push(...this.treeProcessor(nextNode));
const procResult = cached || this.treeProcessor(nextNode, cache);

result.push(...procResult);

if(!cached) {
cache.set(path, procResult)
}
});

result = DependencyCollector.getUniqueWidgets(result);

if (widget) {
this.flatStylesDependencyTree[widget] = [...result];

if (!result.includes(widget)) {
result.push(widget);
}
Expand All @@ -73,62 +84,87 @@ export default class DependencyCollector {
return result;
}

getFullDependencyTree(filePath: string): ScriptsDependencyTree {
let cacheItem = this.scriptsCache[filePath];
const isTsFile = REGEXP_IS_TS_NOT_DTS.test(filePath);
getFullDependencyTree(filePath: string, cache: Map<string, any> = new Map()): ScriptsDependencyTree {
let cacheScriptItem = this.scriptsCache[filePath];
const isTsxFile = REGEXP_IS_TSX_NOT_DTS.test(filePath);
const filePathInProcess = filePathMap.get(filePath);

if (!filePathInProcess && cacheItem === undefined) {
if (!filePathInProcess && cacheScriptItem === undefined) {
filePathMap.set(filePath, busyCache);

precinct.ast = null;

const result = precinct.paperwork(filePath, {
es6: { mixedImports: true },
ts: { skipTypeImports : true }
});

const deps = result.map((relativeDependency: string): string => cabinet({
partial: relativeDependency,
directory: path.resolve(__dirname, '../../../devextreme/js'),
filename: filePath,
ast: precinct.ast,
tsConfig: path.resolve(__dirname, '../../../devextreme/js/__internal/tsconfig.json'),
}))
// NOTE: Workaround for the filing-cabinet issue:
// https://github.com/dependents/node-filing-cabinet/issues/112
.map((path: string) => path.replace(REGEXP_IS_DTS, '.js'))
.filter((path: string): boolean => path !== null
&& existsSync(path)
&& !path.includes('node_modules')
&& !path.includes('viz'));

cacheItem = {
widget: isTsFile
? ''
: DependencyCollector.getWidgetFromAst(precinct.ast),
const deps = result.map((relativeDependency: string): string => {
let absDepPath = relativeDependency

if (relativeDependency.startsWith('.')) {
absDepPath = path.resolve(path.dirname(filePath), relativeDependency)
}

const cachedRes = cache.get(absDepPath);
if (cachedRes) {
return cachedRes;
}

const cabinetResult = cabinet({
partial: relativeDependency,
directory: path.resolve(__dirname, '../../../devextreme/js'),
filename: filePath,
ast: precinct.ast,
tsConfig: path.resolve(__dirname, '../../../devextreme/js/__internal/tsconfig.json'),
});

cache.set(absDepPath, cabinetResult);

return cabinetResult;
})
// NOTE: Workaround for the filing-cabinet issue:
// https://github.com/dependents/node-filing-cabinet/issues/112
.map((path: string) => path.replace(REGEXP_IS_DTS, '.js'))
.filter((path: string): boolean => path !== null
&& existsSync(path)
&& !path.includes('node_modules')
&& !path.includes('viz'));

let widget = '';

if (!isTsxFile) {
widget = precinct.ast
? DependencyCollector.getWidgetFromAst(precinct.ast)
: DependencyCollector.getWidgetFromSource(filePath)
}

cacheScriptItem = {
widget,
dependencies: {},
};

deps.forEach((absolutePath: string) => {
const node = this.getFullDependencyTree(absolutePath);
const node = this.getFullDependencyTree(absolutePath, cache);
if (node) {
cacheItem.dependencies[absolutePath] = node;
cacheScriptItem.dependencies[absolutePath] = node;
}
});

this.scriptsCache[filePath] = cacheItem;
this.scriptsCache[filePath] = cacheScriptItem;
}

return cacheItem;
return cacheScriptItem;
}

validate(): void {
this.themes.forEach((theme) => {
const indexFileName = path.resolve(__dirname, `../../../devextreme-scss/scss/widgets/${theme}/_index.scss`);
const indexContent = readFileSync(indexFileName, 'utf8');
const indexPublicWidgetsList = new WidgetsHandler([], '', {})
.getIndexWidgetItems(indexContent)
.map((item: WidgetItem): string => item.widgetName.toLowerCase())
.sort();
.getIndexWidgetItems(indexContent)
.map((item: WidgetItem): string => item.widgetName.toLowerCase())
.sort();

const dependenciesWidgets = Object.keys(this.flatStylesDependencyTree).sort();

Expand All @@ -141,6 +177,7 @@ export default class DependencyCollector {

collect(): void {
const fullDependencyTree = this.getFullDependencyTree(path.resolve(__dirname, '../../../devextreme/js/bundles/dx.all.js'));

this.treeProcessor(fullDependencyTree);
this.validate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,16 @@ describe('MetadataCollector', () => {

collector.generator.metadata = meta;

let metaContent = 'export const metadata: ThemesMetadata = {\'generic\':[{\'Key\':\'$var\',\'Value\':\'"ON"\'}],\'material\':[],\'fluent\':[]};\n';
let metaContent = `export const metadata: ThemesMetadata = {
'generic': [
{
'Key': '$var',
'Value': '"ON"'
}
],
'material': [],
'fluent': []
};\n`;
metaContent += `export const version: string = '${version}';\n`;
metaContent += 'export const browsersList: Array<string> = [];\n';
metaContent += 'export const dependencies: FlatStylesDependencies = {};\n';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const simpleDependencies: ScriptsDependencyTree = {
dependencies: {},
widget: '',
},
'grid_column.ts': {
dependencies : {},
widget: 'column',
},
},
widget: '',
},
Expand All @@ -79,10 +83,11 @@ const simpleDependencies: ScriptsDependencyTree = {

const tsFilesSet = new Set<string>([
'grid_core',
'grid_column',
]);

const filesContent: { [key: string]: string } = {
'dx.all.js': 'import t from \'./toolbar\';import b from \'./button\'; import grid from \'./data_grid\';',
'dx.all.js': 'import l from \'./list\';import t from \'./toolbar\';import b from \'./button\'; import grid from \'./data_grid\';',
'toolbar.js': 'import m from \'./menu\';import u from \'./utils\';\n// STYLE toolbar',
'menu.js': '// STYLE menu',
'button.js': 'import u from \'./utils\';\n// STYLE button',
Expand All @@ -91,7 +96,8 @@ const filesContent: { [key: string]: string } = {
'render.js': 'import t from \'./utils\';',
'fx.js': '',
'data_grid.js': 'import core from \'./grid_core\'; // STYLE dataGrid',
'grid_core.ts': 'import menu from \'./menu\'; import r from \'./render\';',
'grid_core.ts': 'import menu from \'./menu\'; import r from \'./render\';import col from \'./grid_column\';',
'grid_column.ts': '// STYLE column',

// validation tests
[path.resolve(__dirname, '../../../devextreme-scss/scss/widgets/righttheme/_index.scss')]: '// public widgets\n@use "./toolbar";@use "./button";',
Expand All @@ -115,7 +121,9 @@ const sortDependencies = (dependencies: Record<string, string[]>): void => {
};

jest.mock('fs', () => ({
readFileSync: jest.fn().mockImplementation((path: string): string => filesContent[path] || ''),
readFileSync: jest.fn().mockImplementation((path: string): string => {
return filesContent[path] || ''
}),
existsSync: (path: string): boolean => filesContent[path] !== undefined,
// eslint-disable-next-line spellcheck/spell-checker
realpathSync: (): void => { }, // https://github.com/facebook/jest/issues/10012
Expand Down
6 changes: 6 additions & 0 deletions packages/devextreme-vue/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type AccessibleOptions = Pick<Properties,
"onInitialized" |
"onMessageSend" |
"onOptionChanged" |
"onTypingEnd" |
"onTypingStart" |
"rtlEnabled" |
"user" |
"visible" |
Expand Down Expand Up @@ -46,6 +48,8 @@ const componentConfig = {
onInitialized: Function,
onMessageSend: Function,
onOptionChanged: Function,
onTypingEnd: Function,
onTypingStart: Function,
rtlEnabled: Boolean,
user: Object,
visible: Boolean,
Expand All @@ -69,6 +73,8 @@ const componentConfig = {
"update:onInitialized": null,
"update:onMessageSend": null,
"update:onOptionChanged": null,
"update:onTypingEnd": null,
"update:onTypingStart": null,
"update:rtlEnabled": null,
"update:user": null,
"update:visible": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import {
updateConditionByOperation,
} from './m_utils';

// STYLE filterBuilder

const FILTER_BUILDER_CLASS = 'dx-filterbuilder';
const FILTER_BUILDER_GROUP_CLASS = `${FILTER_BUILDER_CLASS}-group`;
const FILTER_BUILDER_GROUP_ITEM_CLASS = `${FILTER_BUILDER_GROUP_CLASS}-item`;
Expand Down
Loading

0 comments on commit 8565253

Please sign in to comment.