From 78b8bce0d0cf3585480b6941ade47cce626d9997 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 17 Sep 2025 18:29:42 +0400 Subject: [PATCH 1/8] Add angular nested components migration command --- packages/devextreme-cli/index.js | 2 + packages/devextreme-cli/src/application.js | 10 + .../src/applications/application.angular.js | 86 +- packages/devextreme-cli/src/commands.json | 14 + .../devextreme-schematics/package-lock.json | 47 +- packages/devextreme-schematics/package.json | 4 +- .../devextreme-schematics/src/collection.json | 5 + .../src/migrate-nested-components/README.md | 83 ++ .../src/migrate-nested-components/index.ts | 48 + .../mappings/deprecated-nested-map.json | 1088 +++++++++++++++++ .../src/migrate-nested-components/schema.json | 19 + .../template-migrator.ts | 311 +++++ packages/devextreme-schematics/tsconfig.json | 2 + 13 files changed, 1709 insertions(+), 10 deletions(-) create mode 100644 packages/devextreme-schematics/src/migrate-nested-components/README.md create mode 100644 packages/devextreme-schematics/src/migrate-nested-components/index.ts create mode 100644 packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json create mode 100644 packages/devextreme-schematics/src/migrate-nested-components/schema.json create mode 100644 packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts diff --git a/packages/devextreme-cli/index.js b/packages/devextreme-cli/index.js index 6852d8fe5..f0957ca67 100644 --- a/packages/devextreme-cli/index.js +++ b/packages/devextreme-cli/index.js @@ -39,6 +39,8 @@ if(args.help) { const run = async(commands, options) => { if(application.isApplicationCommand(commands[0])) { await application.run(commands, options, devextremeConfig.read()); + } else if(application.isMigrationCommand(commands[0])) { + await application.run(commands, options, { applicationEngine: 'angular' }); } else if(themeBuilder.isThemeBuilderCommand(commands[0])) { options.command = commands[0]; themeBuilder.run(options); diff --git a/packages/devextreme-cli/src/application.js b/packages/devextreme-cli/src/application.js index a5a65228b..bc4c7f291 100644 --- a/packages/devextreme-cli/src/application.js +++ b/packages/devextreme-cli/src/application.js @@ -9,6 +9,10 @@ const isApplicationCommand = (command) => { return [ 'new', 'add' ].includes(command); }; +const isMigrationCommand = (command) => { + return command === 'migrate-nested-components'; +}; + const handleWrongAppType = (appType, command) => { console.error(`The '${appType}' application type is not valid`); printHelp(command); @@ -30,6 +34,11 @@ const createReact = async(appName, options, command) => { }; const run = async(commands, options, devextremeConfig) => { + if(commands[0] === 'migrate-nested-components') { + await angularApplication.migrateNestedComponents(options); + return; + } + if(!commands[1]) { console.error('Command is incomplete. Please specify parameters.'); printHelp(commands[0]); @@ -104,5 +113,6 @@ const run = async(commands, options, devextremeConfig) => { module.exports = { isApplicationCommand, + isMigrationCommand, run }; diff --git a/packages/devextreme-cli/src/applications/application.angular.js b/packages/devextreme-cli/src/applications/application.angular.js index 0f3f72d46..c58c49f9c 100644 --- a/packages/devextreme-cli/src/applications/application.angular.js +++ b/packages/devextreme-cli/src/applications/application.angular.js @@ -53,13 +53,24 @@ async function runNgCommand(commandArguments, commandOptions, commandConfig) { } function localPackageExists(packageName) { + // Check local node_modules first const nodeModulesPath = path.join(process.cwd(), 'node_modules'); - if(!fs.existsSync(nodeModulesPath)) { - return; + if(fs.existsSync(nodeModulesPath)) { + const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json'); + if(fs.existsSync(packageJsonPath)) { + return true; + } } - const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json'); - return fs.existsSync(packageJsonPath); + // Check if globally installed by trying to resolve the package + try { + require.resolve(`${packageName}/package.json`); + return true; + } catch(e) { + // Package not found globally + } + + return false; } const hasSutableNgCli = async() => { @@ -152,6 +163,70 @@ const addView = (viewName, options) => { runSchematicCommand('add-view', schematicOptions); }; +const migrateNestedComponents = async(options = {}) => { + const collectionName = 'devextreme-schematics'; + + // Check if devextreme-schematics is installed + if(!localPackageExists(collectionName)) { + const prompts = require('prompts'); + + console.log(`\nThe '${collectionName}' package is required to run this command.`); + + const response = await prompts({ + type: 'confirm', + name: 'install', + message: `Would you like to install '${collectionName}' now?`, + initial: true + }); + + if(!response.install) { + console.log('Migration cancelled. Please install devextreme-schematics manually:'); + console.log(`npm install -g ${collectionName}@${schematicsVersion}`); + process.exit(1); + } + + console.log(`Installing ${collectionName}@${schematicsVersion}...`); + try { + await runCommand('npm', ['install', '-g', `${collectionName}@${schematicsVersion}`], { stdio: 'inherit' }); + console.log('Installation completed successfully.'); + } catch(error) { + console.error('Failed to install devextreme-schematics. Please install it manually:'); + console.error(`npm install -g ${collectionName}@${schematicsVersion}`); + process.exit(1); + } + } + + const schematicOptions = { + ...options + }; + + if(schematicOptions.include && typeof schematicOptions.include === 'string') { + schematicOptions.include = schematicOptions.include.split(',').map(s => s.trim()); + } + if(schematicOptions.scriptInclude && typeof schematicOptions.scriptInclude === 'string') { + schematicOptions.scriptInclude = schematicOptions.scriptInclude.split(',').map(s => s.trim()); + } + + const commandArguments = ['schematics', `${collectionName}:migrate-nested-components`]; + + const { [depsVersionTagOptionName]: _, ...optionsToArguments } = schematicOptions; // eslint-disable-line no-unused-vars + for(let option in optionsToArguments) { + const value = optionsToArguments[option]; + if(value !== undefined && value !== null && value !== '') { + if(Array.isArray(value)) { + if(value.length > 0) { + commandArguments.push(`--${dasherize(option)}=${value.join(',')}`); + } + } else { + commandArguments.push(`--${dasherize(option)}=${value}`); + } + } + } + + // Use runCommand directly with npx to work outside Angular workspace + return runCommand('npx', commandArguments, { stdio: 'inherit' }); +}; + const changeMainTs = (appPath) => { const filePath = path.join(appPath, 'src', 'main.ts'); @@ -174,5 +249,6 @@ module.exports = { install, create, addTemplate, - addView + addView, + migrateNestedComponents }; diff --git a/packages/devextreme-cli/src/commands.json b/packages/devextreme-cli/src/commands.json index 51e291c3a..d6c71ff15 100644 --- a/packages/devextreme-cli/src/commands.json +++ b/packages/devextreme-cli/src/commands.json @@ -48,6 +48,20 @@ "name": "devextreme-angular", "description": "Add DevExtreme to an Angular application" }] + }, { + "name": "migrate-nested-components", + "description": "Migrate deprecated nested DevExtreme components to new ones", + "usage": "devextreme migrate-nested-components [options]", + "options": [{ + "name": "--include", + "description": "Glob patterns of template files to include (default: **/*.html). Separate multiple patterns with commas." + }, { + "name": "--script-include", + "description": "Glob patterns for TypeScript/JavaScript files to scan for inline @Component({ template }) (default: **/*.ts,**/*.js). Set to empty to disable." + }, { + "name": "--dry", + "description": "Run in dry mode to preview changes without applying them (default: false)" + }] }, { "name": "build-theme", "description": "Build a custom color scheme", diff --git a/packages/devextreme-schematics/package-lock.json b/packages/devextreme-schematics/package-lock.json index 4b6bdc421..a161e9b31 100644 --- a/packages/devextreme-schematics/package-lock.json +++ b/packages/devextreme-schematics/package-lock.json @@ -11,7 +11,9 @@ "dependencies": { "@angular-devkit/core": "^17.3.17", "@angular-devkit/schematics": "^17.3.17", - "@schematics/angular": "^17.3.17" + "@schematics/angular": "^17.3.17", + "parse5": "^7.1.2", + "picomatch": "^4.0.2" }, "devDependencies": { "@types/jasmine": "~3.10.18", @@ -50,6 +52,18 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/core/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -472,6 +486,18 @@ "node": ">=0.3.1" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -789,6 +815,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -811,9 +849,10 @@ "dev": true }, "node_modules/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { "node": ">=12" }, diff --git a/packages/devextreme-schematics/package.json b/packages/devextreme-schematics/package.json index 475047c81..036aad279 100644 --- a/packages/devextreme-schematics/package.json +++ b/packages/devextreme-schematics/package.json @@ -24,7 +24,9 @@ "dependencies": { "@angular-devkit/core": "^17.3.17", "@angular-devkit/schematics": "^17.3.17", - "@schematics/angular": "^17.3.17" + "@schematics/angular": "^17.3.17", + "parse5": "^7.1.2", + "picomatch": "^4.0.2" }, "devDependencies": { "@types/jasmine": "~3.10.18", diff --git a/packages/devextreme-schematics/src/collection.json b/packages/devextreme-schematics/src/collection.json index 30f0bdcc5..2e5713370 100644 --- a/packages/devextreme-schematics/src/collection.json +++ b/packages/devextreme-schematics/src/collection.json @@ -24,6 +24,11 @@ "description": "Add a new view to app-template.", "factory": "./add-view/index", "schema": "./add-view/schema.json" + }, + "migrate-nested-components": { + "description": "Migrate nested DevExtreme components to the latest version.", + "factory": "./migrate-nested-components/index#migrateNestedComponents", + "schema": "./migrate-nested-components/schema.json" } } } diff --git a/packages/devextreme-schematics/src/migrate-nested-components/README.md b/packages/devextreme-schematics/src/migrate-nested-components/README.md new file mode 100644 index 000000000..16b77205d --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-nested-components/README.md @@ -0,0 +1,83 @@ +# migrate-nested-components + +A schematic that migrates deprecated nested DevExtreme components to the new structure. + +## Description + +This schematic automatically migrates your Angular application to use the latest DevExtreme component structure by replacing deprecated nested components with their new equivalents. The migration command is available as a top-level DevExtreme CLI command and can be run from any directory. + +## Usage + +### Via DevExtreme CLI (Recommended) + +The migration command can be run from any directory and will work both inside and outside Angular workspaces: + +```bash +devextreme migrate-nested-components +``` + +### Options (All Optional) + +- `--include`: Glob patterns of template files to include (default: `**/*.html`). Separate multiple patterns with commas. +- `--script-include`: Glob patterns for TypeScript/JavaScript files to scan for inline `@Component({ template })` (default: `**/*.ts,**/*.js`). Set to empty to disable. +- `--dry`: Run in dry mode to preview changes without applying them (default: `false`) + +### Examples + +```bash +# Migrate all HTML templates and inline templates (using defaults) +devextreme migrate-nested-components + +# Migrate only specific files +devextreme migrate-nested-components --include="src/app/**/*.html,src/shared/**/*.html" + +# Preview changes without applying them +devextreme migrate-nested-components --dry + +# Migrate only HTML templates, skip inline templates +devextreme migrate-nested-components --script-include="" + +# Combine multiple options +devextreme migrate-nested-components --include="*.html,*.ts" --dry +``` + +### Via Angular CLI + +Alternatively, you can run the schematic directly with Angular CLI: + +```bash +ng g devextreme-schematics:migrate-nested-components +``` + +## What it does + +This schematic automatically updates your templates to replace deprecated nested components with the new component structure. For example: + +**Before:** +```html + + + + +``` + +**After:** +```html + + + + +``` + +## Requirements + +- Node.js and npm/yarn installed +- Angular application with DevExtreme components (when running inside a workspace) +- DevExtreme Angular version that supports the new component structure + +## Notes + +- The command can be run from any directory, not just Angular workspaces +- When run outside an Angular workspace, the underlying Angular CLI will show appropriate error messages +- All options are optional and have sensible defaults +- The command uses the latest version of `devextreme-schematics` automatically diff --git a/packages/devextreme-schematics/src/migrate-nested-components/index.ts b/packages/devextreme-schematics/src/migrate-nested-components/index.ts new file mode 100644 index 000000000..e434d5cd7 --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-nested-components/index.ts @@ -0,0 +1,48 @@ +import { Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; +import { applyHostAwareTemplateMigrations, applyInlineComponentTemplateMigrations } from './template-migrator'; +import mapping from './mappings/deprecated-nested-map.json'; + +export interface Options { + include?: string[]; + dry?: boolean; + scriptInclude?: string[]; +} + +export function migrateNestedComponents(options: Options = {}): Rule { + const include = options.include?.length ? options.include : ['**/*.html']; + + return async (tree: Tree, ctx: SchematicContext): Promise => { + ctx.logger.info(`[nested-migrator] Starting…`); + + type HostMap = Record>; + const hostMap = mapping as unknown as HostMap; + + const processedMapping = Object.entries(hostMap).map(([hostComponentName, map]) => { + const hostSelector = map._hostSelector ?? componentToSelectorGuess(hostComponentName); + const nestedMap: Record = Object.fromEntries( + Object.entries(map).filter(([k]) => k !== '_hostSelector') + ); + return { hostSelector, nestedMap }; + }); + + // External HTML templates + await applyHostAwareTemplateMigrations(tree, { + includeGlobs: include, + rules: processedMapping + }, { dryRun: !!options.dry, logger: ctx.logger }); + + // Inline templates inside component decorators (ts/js) + const scriptGlobs = options.scriptInclude === undefined ? ['**/*.ts', '**/*.js'] : options.scriptInclude; + await applyInlineComponentTemplateMigrations(tree, { + includeGlobs: [], rules: processedMapping + }, { dryRun: !!options.dry, logger: ctx.logger }, scriptGlobs); + + ctx.logger.info(`[nested-migrator] Done.`); + }; +} + +// Fallback if _hostSelector is missing: "DxFooBarComponent" -> "dx-foo-bar" +function componentToSelectorGuess(componentName: string): string { + const core = componentName.replace(/Component$/, '').replace(/^Dx/, 'dx-'); + return core.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); +} diff --git a/packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json b/packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json new file mode 100644 index 000000000..362637e3f --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json @@ -0,0 +1,1088 @@ +{ + "_generated": true, + "_description": "Mapping: host component -> { _hostSelector?: string, generic nested selector : component-specific selector }", + "_note": "Do not edit manually; run scripts/generate-deprecated-nested-map.mjs", + "DxAccordionComponent": { + "_hostSelector": "dx-accordion", + "dxi-item": "dxi-accordion-item" + }, + "DxActionSheetComponent": { + "_hostSelector": "dx-action-sheet", + "dxi-item": "dxi-action-sheet-item" + }, + "DxAutocompleteComponent": { + "_hostSelector": "dx-autocomplete", + "dxi-button": "dxi-autocomplete-button", + "dxi-item": "dxi-autocomplete-item", + "dxi-toolbar-item": "dxi-autocomplete-toolbar-item", + "dxo-animation": "dxo-autocomplete-animation", + "dxo-at": "dxo-autocomplete-at", + "dxo-boundary-offset": "dxo-autocomplete-boundary-offset", + "dxo-collision": "dxo-autocomplete-collision", + "dxo-drop-down-options": "dxo-autocomplete-drop-down-options", + "dxo-from": "dxo-autocomplete-from", + "dxo-hide": "dxo-autocomplete-hide", + "dxo-my": "dxo-autocomplete-my", + "dxo-offset": "dxo-autocomplete-offset", + "dxo-options": "dxo-autocomplete-options", + "dxo-position": "dxo-autocomplete-position", + "dxo-show": "dxo-autocomplete-show", + "dxo-to": "dxo-autocomplete-to" + }, + "DxBarGaugeComponent": { + "_hostSelector": "dx-bar-gauge", + "dxo-animation": "dxo-bar-gauge-animation", + "dxo-border": "dxo-bar-gauge-border", + "dxo-export": "dxo-bar-gauge-export", + "dxo-font": "dxo-bar-gauge-font", + "dxo-format": "dxo-bar-gauge-format", + "dxo-geometry": "dxo-bar-gauge-geometry", + "dxo-item-text-format": "dxo-bar-gauge-item-text-format", + "dxo-label": "dxo-bar-gauge-label", + "dxo-legend": "dxo-bar-gauge-legend", + "dxo-loading-indicator": "dxo-bar-gauge-loading-indicator", + "dxo-margin": "dxo-bar-gauge-margin", + "dxo-shadow": "dxo-bar-gauge-shadow", + "dxo-size": "dxo-bar-gauge-size", + "dxo-subtitle": "dxo-bar-gauge-subtitle", + "dxo-title": "dxo-bar-gauge-title", + "dxo-tooltip": "dxo-bar-gauge-tooltip" + }, + "DxBoxComponent": { + "_hostSelector": "dx-box", + "dxi-item": "dxi-box-item" + }, + "DxBulletComponent": { + "_hostSelector": "dx-bullet", + "dxo-border": "dxo-bullet-border", + "dxo-font": "dxo-bullet-font", + "dxo-format": "dxo-bullet-format", + "dxo-margin": "dxo-bullet-margin", + "dxo-shadow": "dxo-bullet-shadow", + "dxo-size": "dxo-bullet-size", + "dxo-tooltip": "dxo-bullet-tooltip" + }, + "DxButtonGroupComponent": { + "_hostSelector": "dx-button-group", + "dxi-item": "dxi-button-group-item" + }, + "DxCardViewComponent": { + "_hostSelector": "dx-card-view", + "dxi-change": "dxi-card-view-change", + "dxi-column": "dxi-card-view-column", + "dxi-custom-operation": "dxi-card-view-custom-operation", + "dxi-field": "dxi-card-view-field", + "dxi-group-item": "dxi-card-view-group-item", + "dxi-item": "dxi-card-view-item", + "dxi-tab": "dxi-card-view-tab", + "dxi-toolbar-item": "dxi-card-view-toolbar-item", + "dxi-validation-rule": "dxi-card-view-validation-rule", + "dxo-animation": "dxo-card-view-animation", + "dxo-at": "dxo-card-view-at", + "dxo-boundary-offset": "dxo-card-view-boundary-offset", + "dxo-button-options": "dxo-card-view-button-options", + "dxo-col-count-by-screen": "dxo-card-view-col-count-by-screen", + "dxo-collision": "dxo-card-view-collision", + "dxo-column-chooser": "dxo-card-view-column-chooser", + "dxo-editing": "dxo-card-view-editing", + "dxo-filter-builder": "dxo-card-view-filter-builder", + "dxo-filter-operation-descriptions": "dxo-card-view-filter-operation-descriptions", + "dxo-filter-panel": "dxo-card-view-filter-panel", + "dxo-form": "dxo-card-view-form", + "dxo-form-item": "dxo-card-view-form-item", + "dxo-format": "dxo-card-view-format", + "dxo-from": "dxo-card-view-from", + "dxo-group-operation-descriptions": "dxo-card-view-group-operation-descriptions", + "dxo-header-filter": "dxo-card-view-header-filter", + "dxo-hide": "dxo-card-view-hide", + "dxo-label": "dxo-card-view-label", + "dxo-load-panel": "dxo-card-view-load-panel", + "dxo-lookup": "dxo-card-view-lookup", + "dxo-my": "dxo-card-view-my", + "dxo-offset": "dxo-card-view-offset", + "dxo-pager": "dxo-card-view-pager", + "dxo-paging": "dxo-card-view-paging", + "dxo-position": "dxo-card-view-position", + "dxo-remote-operations": "dxo-card-view-remote-operations", + "dxo-scrolling": "dxo-card-view-scrolling", + "dxo-search": "dxo-card-view-search", + "dxo-search-panel": "dxo-card-view-search-panel", + "dxo-selection": "dxo-card-view-selection", + "dxo-show": "dxo-card-view-show", + "dxo-sorting": "dxo-card-view-sorting", + "dxo-tab-panel-options": "dxo-card-view-tab-panel-options", + "dxo-texts": "dxo-card-view-texts", + "dxo-to": "dxo-card-view-to", + "dxo-toolbar": "dxo-card-view-toolbar" + }, + "DxChartComponent": { + "_hostSelector": "dx-chart", + "dxi-annotation": "dxi-chart-annotation", + "dxi-break": "dxi-chart-break", + "dxi-constant-line": "dxi-chart-constant-line", + "dxi-pane": "dxi-chart-pane", + "dxi-series": "dxi-chart-series", + "dxi-strip": "dxi-chart-strip", + "dxi-value-axis": "dxi-chart-value-axis", + "dxo-adaptive-layout": "dxo-chart-adaptive-layout", + "dxo-aggregation": "dxo-chart-aggregation", + "dxo-aggregation-interval": "dxo-chart-aggregation-interval", + "dxo-animation": "dxo-chart-animation", + "dxo-argument-axis": "dxo-chart-argument-axis", + "dxo-argument-format": "dxo-chart-argument-format", + "dxo-background-color": "dxo-chart-background-color", + "dxo-border": "dxo-chart-border", + "dxo-break-style": "dxo-chart-break-style", + "dxo-color": "dxo-chart-color", + "dxo-common-annotation-settings": "dxo-chart-common-annotation-settings", + "dxo-common-axis-settings": "dxo-chart-common-axis-settings", + "dxo-common-pane-settings": "dxo-chart-common-pane-settings", + "dxo-common-series-settings": "dxo-chart-common-series-settings", + "dxo-connector": "dxo-chart-connector", + "dxo-constant-line-style": "dxo-chart-constant-line-style", + "dxo-crosshair": "dxo-chart-crosshair", + "dxo-data-prepare-settings": "dxo-chart-data-prepare-settings", + "dxo-drag-box-style": "dxo-chart-drag-box-style", + "dxo-export": "dxo-chart-export", + "dxo-font": "dxo-chart-font", + "dxo-format": "dxo-chart-format", + "dxo-grid": "dxo-chart-grid", + "dxo-hatching": "dxo-chart-hatching", + "dxo-height": "dxo-chart-height", + "dxo-horizontal-line": "dxo-chart-horizontal-line", + "dxo-hover-style": "dxo-chart-hover-style", + "dxo-image": "dxo-chart-image", + "dxo-label": "dxo-chart-label", + "dxo-legend": "dxo-chart-legend", + "dxo-loading-indicator": "dxo-chart-loading-indicator", + "dxo-margin": "dxo-chart-margin", + "dxo-min-visual-range-length": "dxo-chart-min-visual-range-length", + "dxo-minor-grid": "dxo-chart-minor-grid", + "dxo-minor-tick": "dxo-chart-minor-tick", + "dxo-minor-tick-interval": "dxo-chart-minor-tick-interval", + "dxo-point": "dxo-chart-point", + "dxo-reduction": "dxo-chart-reduction", + "dxo-scroll-bar": "dxo-chart-scroll-bar", + "dxo-selection-style": "dxo-chart-selection-style", + "dxo-series-template": "dxo-chart-series-template", + "dxo-shadow": "dxo-chart-shadow", + "dxo-size": "dxo-chart-size", + "dxo-strip-style": "dxo-chart-strip-style", + "dxo-subtitle": "dxo-chart-subtitle", + "dxo-tick": "dxo-chart-tick", + "dxo-tick-interval": "dxo-chart-tick-interval", + "dxo-title": "dxo-chart-title", + "dxo-tooltip": "dxo-chart-tooltip", + "dxo-url": "dxo-chart-url", + "dxo-value-error-bar": "dxo-chart-value-error-bar", + "dxo-vertical-line": "dxo-chart-vertical-line", + "dxo-width": "dxo-chart-width", + "dxo-zoom-and-pan": "dxo-chart-zoom-and-pan" + }, + "DxChatComponent": { + "_hostSelector": "dx-chat", + "dxi-alert": "dxi-chat-alert", + "dxi-item": "dxi-chat-item", + "dxi-typing-user": "dxi-chat-typing-user", + "dxo-author": "dxo-chat-author", + "dxo-day-header-format": "dxo-chat-day-header-format", + "dxo-editing": "dxo-chat-editing", + "dxo-message-timestamp-format": "dxo-chat-message-timestamp-format", + "dxo-user": "dxo-chat-user" + }, + "DxCircularGaugeComponent": { + "_hostSelector": "dx-circular-gauge", + "dxi-range": "dxi-circular-gauge-range", + "dxo-animation": "dxo-circular-gauge-animation", + "dxo-background-color": "dxo-circular-gauge-background-color", + "dxo-border": "dxo-circular-gauge-border", + "dxo-color": "dxo-circular-gauge-color", + "dxo-export": "dxo-circular-gauge-export", + "dxo-font": "dxo-circular-gauge-font", + "dxo-format": "dxo-circular-gauge-format", + "dxo-geometry": "dxo-circular-gauge-geometry", + "dxo-label": "dxo-circular-gauge-label", + "dxo-loading-indicator": "dxo-circular-gauge-loading-indicator", + "dxo-margin": "dxo-circular-gauge-margin", + "dxo-minor-tick": "dxo-circular-gauge-minor-tick", + "dxo-range-container": "dxo-circular-gauge-range-container", + "dxo-scale": "dxo-circular-gauge-scale", + "dxo-shadow": "dxo-circular-gauge-shadow", + "dxo-size": "dxo-circular-gauge-size", + "dxo-subtitle": "dxo-circular-gauge-subtitle", + "dxo-subvalue-indicator": "dxo-circular-gauge-subvalue-indicator", + "dxo-text": "dxo-circular-gauge-text", + "dxo-tick": "dxo-circular-gauge-tick", + "dxo-title": "dxo-circular-gauge-title", + "dxo-tooltip": "dxo-circular-gauge-tooltip", + "dxo-value-indicator": "dxo-circular-gauge-value-indicator" + }, + "DxColorBoxComponent": { + "_hostSelector": "dx-color-box", + "dxi-button": "dxi-color-box-button", + "dxi-toolbar-item": "dxi-color-box-toolbar-item", + "dxo-animation": "dxo-color-box-animation", + "dxo-at": "dxo-color-box-at", + "dxo-boundary-offset": "dxo-color-box-boundary-offset", + "dxo-collision": "dxo-color-box-collision", + "dxo-drop-down-options": "dxo-color-box-drop-down-options", + "dxo-from": "dxo-color-box-from", + "dxo-hide": "dxo-color-box-hide", + "dxo-my": "dxo-color-box-my", + "dxo-offset": "dxo-color-box-offset", + "dxo-options": "dxo-color-box-options", + "dxo-position": "dxo-color-box-position", + "dxo-show": "dxo-color-box-show", + "dxo-to": "dxo-color-box-to" + }, + "DxContextMenuComponent": { + "_hostSelector": "dx-context-menu", + "dxi-item": "dxi-context-menu-item", + "dxo-animation": "dxo-context-menu-animation", + "dxo-at": "dxo-context-menu-at", + "dxo-boundary-offset": "dxo-context-menu-boundary-offset", + "dxo-collision": "dxo-context-menu-collision", + "dxo-delay": "dxo-context-menu-delay", + "dxo-from": "dxo-context-menu-from", + "dxo-hide": "dxo-context-menu-hide", + "dxo-my": "dxo-context-menu-my", + "dxo-offset": "dxo-context-menu-offset", + "dxo-position": "dxo-context-menu-position", + "dxo-show": "dxo-context-menu-show", + "dxo-show-event": "dxo-context-menu-show-event", + "dxo-show-submenu-mode": "dxo-context-menu-show-submenu-mode", + "dxo-to": "dxo-context-menu-to" + }, + "DxDataGridComponent": { + "_hostSelector": "dx-data-grid", + "dxi-button": "dxi-data-grid-button", + "dxi-change": "dxi-data-grid-change", + "dxi-column": "dxi-data-grid-column", + "dxi-custom-operation": "dxi-data-grid-custom-operation", + "dxi-field": "dxi-data-grid-field", + "dxi-group-item": "dxi-data-grid-group-item", + "dxi-item": "dxi-data-grid-item", + "dxi-sort-by-group-summary-info": "dxi-data-grid-sort-by-group-summary-info", + "dxi-toolbar-item": "dxi-data-grid-toolbar-item", + "dxi-total-item": "dxi-data-grid-total-item", + "dxi-validation-rule": "dxi-data-grid-validation-rule", + "dxo-animation": "dxo-data-grid-animation", + "dxo-at": "dxo-data-grid-at", + "dxo-boundary-offset": "dxo-data-grid-boundary-offset", + "dxo-col-count-by-screen": "dxo-data-grid-col-count-by-screen", + "dxo-collision": "dxo-data-grid-collision", + "dxo-column-chooser": "dxo-data-grid-column-chooser", + "dxo-column-fixing": "dxo-data-grid-column-fixing", + "dxo-cursor-offset": "dxo-data-grid-cursor-offset", + "dxo-editing": "dxo-data-grid-editing", + "dxo-export": "dxo-data-grid-export", + "dxo-filter-builder": "dxo-data-grid-filter-builder", + "dxo-filter-builder-popup": "dxo-data-grid-filter-builder-popup", + "dxo-filter-operation-descriptions": "dxo-data-grid-filter-operation-descriptions", + "dxo-filter-panel": "dxo-data-grid-filter-panel", + "dxo-filter-row": "dxo-data-grid-filter-row", + "dxo-form": "dxo-data-grid-form", + "dxo-form-item": "dxo-data-grid-form-item", + "dxo-format": "dxo-data-grid-format", + "dxo-from": "dxo-data-grid-from", + "dxo-group-operation-descriptions": "dxo-data-grid-group-operation-descriptions", + "dxo-group-panel": "dxo-data-grid-group-panel", + "dxo-grouping": "dxo-data-grid-grouping", + "dxo-header-filter": "dxo-data-grid-header-filter", + "dxo-hide": "dxo-data-grid-hide", + "dxo-icons": "dxo-data-grid-icons", + "dxo-keyboard-navigation": "dxo-data-grid-keyboard-navigation", + "dxo-label": "dxo-data-grid-label", + "dxo-load-panel": "dxo-data-grid-load-panel", + "dxo-lookup": "dxo-data-grid-lookup", + "dxo-master-detail": "dxo-data-grid-master-detail", + "dxo-my": "dxo-data-grid-my", + "dxo-offset": "dxo-data-grid-offset", + "dxo-operation-descriptions": "dxo-data-grid-operation-descriptions", + "dxo-pager": "dxo-data-grid-pager", + "dxo-paging": "dxo-data-grid-paging", + "dxo-popup": "dxo-data-grid-popup", + "dxo-position": "dxo-data-grid-position", + "dxo-remote-operations": "dxo-data-grid-remote-operations", + "dxo-row-dragging": "dxo-data-grid-row-dragging", + "dxo-scrolling": "dxo-data-grid-scrolling", + "dxo-search": "dxo-data-grid-search", + "dxo-search-panel": "dxo-data-grid-search-panel", + "dxo-selection": "dxo-data-grid-selection", + "dxo-show": "dxo-data-grid-show", + "dxo-sorting": "dxo-data-grid-sorting", + "dxo-state-storing": "dxo-data-grid-state-storing", + "dxo-summary": "dxo-data-grid-summary", + "dxo-texts": "dxo-data-grid-texts", + "dxo-to": "dxo-data-grid-to", + "dxo-toolbar": "dxo-data-grid-toolbar", + "dxo-value-format": "dxo-data-grid-value-format" + }, + "DxDateBoxComponent": { + "_hostSelector": "dx-date-box", + "dxi-button": "dxi-date-box-button", + "dxi-toolbar-item": "dxi-date-box-toolbar-item", + "dxo-animation": "dxo-date-box-animation", + "dxo-at": "dxo-date-box-at", + "dxo-boundary-offset": "dxo-date-box-boundary-offset", + "dxo-calendar-options": "dxo-date-box-calendar-options", + "dxo-collision": "dxo-date-box-collision", + "dxo-display-format": "dxo-date-box-display-format", + "dxo-drop-down-options": "dxo-date-box-drop-down-options", + "dxo-from": "dxo-date-box-from", + "dxo-hide": "dxo-date-box-hide", + "dxo-my": "dxo-date-box-my", + "dxo-offset": "dxo-date-box-offset", + "dxo-options": "dxo-date-box-options", + "dxo-position": "dxo-date-box-position", + "dxo-show": "dxo-date-box-show", + "dxo-to": "dxo-date-box-to" + }, + "DxDateRangeBoxComponent": { + "_hostSelector": "dx-date-range-box", + "dxi-button": "dxi-date-range-box-button", + "dxi-toolbar-item": "dxi-date-range-box-toolbar-item", + "dxo-animation": "dxo-date-range-box-animation", + "dxo-at": "dxo-date-range-box-at", + "dxo-boundary-offset": "dxo-date-range-box-boundary-offset", + "dxo-calendar-options": "dxo-date-range-box-calendar-options", + "dxo-collision": "dxo-date-range-box-collision", + "dxo-display-format": "dxo-date-range-box-display-format", + "dxo-drop-down-options": "dxo-date-range-box-drop-down-options", + "dxo-from": "dxo-date-range-box-from", + "dxo-hide": "dxo-date-range-box-hide", + "dxo-my": "dxo-date-range-box-my", + "dxo-offset": "dxo-date-range-box-offset", + "dxo-options": "dxo-date-range-box-options", + "dxo-position": "dxo-date-range-box-position", + "dxo-show": "dxo-date-range-box-show", + "dxo-to": "dxo-date-range-box-to" + }, + "DxDeferRenderingComponent": { + "_hostSelector": "dx-defer-rendering", + "dxo-animation": "dxo-defer-rendering-animation", + "dxo-at": "dxo-defer-rendering-at", + "dxo-boundary-offset": "dxo-defer-rendering-boundary-offset", + "dxo-collision": "dxo-defer-rendering-collision", + "dxo-from": "dxo-defer-rendering-from", + "dxo-my": "dxo-defer-rendering-my", + "dxo-offset": "dxo-defer-rendering-offset", + "dxo-position": "dxo-defer-rendering-position", + "dxo-to": "dxo-defer-rendering-to" + }, + "DxDiagramComponent": { + "_hostSelector": "dx-diagram", + "dxi-command": "dxi-diagram-command", + "dxi-connection-point": "dxi-diagram-connection-point", + "dxi-custom-shape": "dxi-diagram-custom-shape", + "dxi-group": "dxi-diagram-group", + "dxi-item": "dxi-diagram-item", + "dxi-tab": "dxi-diagram-tab", + "dxo-auto-layout": "dxo-diagram-auto-layout", + "dxo-context-menu": "dxo-diagram-context-menu", + "dxo-context-toolbox": "dxo-diagram-context-toolbox", + "dxo-default-item-properties": "dxo-diagram-default-item-properties", + "dxo-edges": "dxo-diagram-edges", + "dxo-editing": "dxo-diagram-editing", + "dxo-export": "dxo-diagram-export", + "dxo-grid-size": "dxo-diagram-grid-size", + "dxo-history-toolbar": "dxo-diagram-history-toolbar", + "dxo-main-toolbar": "dxo-diagram-main-toolbar", + "dxo-nodes": "dxo-diagram-nodes", + "dxo-page-size": "dxo-diagram-page-size", + "dxo-properties-panel": "dxo-diagram-properties-panel", + "dxo-toolbox": "dxo-diagram-toolbox", + "dxo-view-toolbar": "dxo-diagram-view-toolbar", + "dxo-zoom-level": "dxo-diagram-zoom-level" + }, + "DxDraggableComponent": { + "_hostSelector": "dx-draggable", + "dxo-cursor-offset": "dxo-draggable-cursor-offset" + }, + "DxDropDownBoxComponent": { + "_hostSelector": "dx-drop-down-box", + "dxi-button": "dxi-drop-down-box-button", + "dxi-toolbar-item": "dxi-drop-down-box-toolbar-item", + "dxo-animation": "dxo-drop-down-box-animation", + "dxo-at": "dxo-drop-down-box-at", + "dxo-boundary-offset": "dxo-drop-down-box-boundary-offset", + "dxo-collision": "dxo-drop-down-box-collision", + "dxo-drop-down-options": "dxo-drop-down-box-drop-down-options", + "dxo-from": "dxo-drop-down-box-from", + "dxo-hide": "dxo-drop-down-box-hide", + "dxo-my": "dxo-drop-down-box-my", + "dxo-offset": "dxo-drop-down-box-offset", + "dxo-options": "dxo-drop-down-box-options", + "dxo-position": "dxo-drop-down-box-position", + "dxo-show": "dxo-drop-down-box-show", + "dxo-to": "dxo-drop-down-box-to" + }, + "DxDropDownButtonComponent": { + "_hostSelector": "dx-drop-down-button", + "dxi-item": "dxi-drop-down-button-item", + "dxi-toolbar-item": "dxi-drop-down-button-toolbar-item", + "dxo-animation": "dxo-drop-down-button-animation", + "dxo-at": "dxo-drop-down-button-at", + "dxo-boundary-offset": "dxo-drop-down-button-boundary-offset", + "dxo-collision": "dxo-drop-down-button-collision", + "dxo-drop-down-options": "dxo-drop-down-button-drop-down-options", + "dxo-from": "dxo-drop-down-button-from", + "dxo-hide": "dxo-drop-down-button-hide", + "dxo-my": "dxo-drop-down-button-my", + "dxo-offset": "dxo-drop-down-button-offset", + "dxo-position": "dxo-drop-down-button-position", + "dxo-show": "dxo-drop-down-button-show", + "dxo-to": "dxo-drop-down-button-to" + }, + "DxFileManagerComponent": { + "_hostSelector": "dx-file-manager", + "dxi-column": "dxi-file-manager-column", + "dxi-file-selection-item": "dxi-file-manager-file-selection-item", + "dxi-item": "dxi-file-manager-item", + "dxi-toolbar-item": "dxi-file-manager-toolbar-item", + "dxo-context-menu": "dxo-file-manager-context-menu", + "dxo-details": "dxo-file-manager-details", + "dxo-item-view": "dxo-file-manager-item-view", + "dxo-notifications": "dxo-file-manager-notifications", + "dxo-permissions": "dxo-file-manager-permissions", + "dxo-toolbar": "dxo-file-manager-toolbar", + "dxo-upload": "dxo-file-manager-upload" + }, + "DxFilterBuilderComponent": { + "_hostSelector": "dx-filter-builder", + "dxi-custom-operation": "dxi-filter-builder-custom-operation", + "dxi-field": "dxi-filter-builder-field", + "dxo-filter-operation-descriptions": "dxo-filter-builder-filter-operation-descriptions", + "dxo-format": "dxo-filter-builder-format", + "dxo-group-operation-descriptions": "dxo-filter-builder-group-operation-descriptions", + "dxo-lookup": "dxo-filter-builder-lookup" + }, + "DxFormComponent": { + "_hostSelector": "dx-form", + "dxi-group-item": "dxi-form-group-item", + "dxi-item": "dxi-form-item", + "dxi-tab": "dxi-form-tab", + "dxi-validation-rule": "dxi-form-validation-rule", + "dxo-button-options": "dxo-form-button-options", + "dxo-col-count-by-screen": "dxo-form-col-count-by-screen", + "dxo-label": "dxo-form-label", + "dxo-tab-panel-options": "dxo-form-tab-panel-options" + }, + "DxFunnelComponent": { + "_hostSelector": "dx-funnel", + "dxo-adaptive-layout": "dxo-funnel-adaptive-layout", + "dxo-border": "dxo-funnel-border", + "dxo-connector": "dxo-funnel-connector", + "dxo-export": "dxo-funnel-export", + "dxo-font": "dxo-funnel-font", + "dxo-format": "dxo-funnel-format", + "dxo-hatching": "dxo-funnel-hatching", + "dxo-hover-style": "dxo-funnel-hover-style", + "dxo-item": "dxo-funnel-item", + "dxo-label": "dxo-funnel-label", + "dxo-legend": "dxo-funnel-legend", + "dxo-loading-indicator": "dxo-funnel-loading-indicator", + "dxo-margin": "dxo-funnel-margin", + "dxo-selection-style": "dxo-funnel-selection-style", + "dxo-shadow": "dxo-funnel-shadow", + "dxo-size": "dxo-funnel-size", + "dxo-subtitle": "dxo-funnel-subtitle", + "dxo-title": "dxo-funnel-title", + "dxo-tooltip": "dxo-funnel-tooltip" + }, + "DxGalleryComponent": { + "_hostSelector": "dx-gallery", + "dxi-item": "dxi-gallery-item" + }, + "DxGanttComponent": { + "_hostSelector": "dx-gantt", + "dxi-column": "dxi-gantt-column", + "dxi-item": "dxi-gantt-item", + "dxi-strip-line": "dxi-gantt-strip-line", + "dxi-toolbar-item": "dxi-gantt-toolbar-item", + "dxo-context-menu": "dxo-gantt-context-menu", + "dxo-dependencies": "dxo-gantt-dependencies", + "dxo-editing": "dxo-gantt-editing", + "dxo-filter-row": "dxo-gantt-filter-row", + "dxo-format": "dxo-gantt-format", + "dxo-header-filter": "dxo-gantt-header-filter", + "dxo-operation-descriptions": "dxo-gantt-operation-descriptions", + "dxo-resource-assignments": "dxo-gantt-resource-assignments", + "dxo-resources": "dxo-gantt-resources", + "dxo-scale-type-range": "dxo-gantt-scale-type-range", + "dxo-search": "dxo-gantt-search", + "dxo-sorting": "dxo-gantt-sorting", + "dxo-tasks": "dxo-gantt-tasks", + "dxo-texts": "dxo-gantt-texts", + "dxo-toolbar": "dxo-gantt-toolbar", + "dxo-validation": "dxo-gantt-validation" + }, + "DxHtmlEditorComponent": { + "_hostSelector": "dx-html-editor", + "dxi-command": "dxi-html-editor-command", + "dxi-item": "dxi-html-editor-item", + "dxi-mention": "dxi-html-editor-mention", + "dxi-tab": "dxi-html-editor-tab", + "dxi-toolbar-item": "dxi-html-editor-toolbar-item", + "dxo-converter": "dxo-html-editor-converter", + "dxo-file-uploader-options": "dxo-html-editor-file-uploader-options", + "dxo-image-upload": "dxo-html-editor-image-upload", + "dxo-media-resizing": "dxo-html-editor-media-resizing", + "dxo-table-context-menu": "dxo-html-editor-table-context-menu", + "dxo-table-resizing": "dxo-html-editor-table-resizing", + "dxo-toolbar": "dxo-html-editor-toolbar", + "dxo-variables": "dxo-html-editor-variables" + }, + "DxLinearGaugeComponent": { + "_hostSelector": "dx-linear-gauge", + "dxi-range": "dxi-linear-gauge-range", + "dxo-animation": "dxo-linear-gauge-animation", + "dxo-background-color": "dxo-linear-gauge-background-color", + "dxo-border": "dxo-linear-gauge-border", + "dxo-color": "dxo-linear-gauge-color", + "dxo-export": "dxo-linear-gauge-export", + "dxo-font": "dxo-linear-gauge-font", + "dxo-format": "dxo-linear-gauge-format", + "dxo-geometry": "dxo-linear-gauge-geometry", + "dxo-label": "dxo-linear-gauge-label", + "dxo-loading-indicator": "dxo-linear-gauge-loading-indicator", + "dxo-margin": "dxo-linear-gauge-margin", + "dxo-minor-tick": "dxo-linear-gauge-minor-tick", + "dxo-range-container": "dxo-linear-gauge-range-container", + "dxo-scale": "dxo-linear-gauge-scale", + "dxo-shadow": "dxo-linear-gauge-shadow", + "dxo-size": "dxo-linear-gauge-size", + "dxo-subtitle": "dxo-linear-gauge-subtitle", + "dxo-subvalue-indicator": "dxo-linear-gauge-subvalue-indicator", + "dxo-text": "dxo-linear-gauge-text", + "dxo-tick": "dxo-linear-gauge-tick", + "dxo-title": "dxo-linear-gauge-title", + "dxo-tooltip": "dxo-linear-gauge-tooltip", + "dxo-value-indicator": "dxo-linear-gauge-value-indicator", + "dxo-width": "dxo-linear-gauge-width" + }, + "DxListComponent": { + "_hostSelector": "dx-list", + "dxi-button": "dxi-list-button", + "dxi-item": "dxi-list-item", + "dxi-menu-item": "dxi-list-menu-item", + "dxo-cursor-offset": "dxo-list-cursor-offset", + "dxo-item-dragging": "dxo-list-item-dragging", + "dxo-options": "dxo-list-options", + "dxo-search-editor-options": "dxo-list-search-editor-options" + }, + "DxLoadPanelComponent": { + "_hostSelector": "dx-load-panel", + "dxo-animation": "dxo-load-panel-animation", + "dxo-at": "dxo-load-panel-at", + "dxo-boundary-offset": "dxo-load-panel-boundary-offset", + "dxo-collision": "dxo-load-panel-collision", + "dxo-from": "dxo-load-panel-from", + "dxo-hide": "dxo-load-panel-hide", + "dxo-my": "dxo-load-panel-my", + "dxo-offset": "dxo-load-panel-offset", + "dxo-position": "dxo-load-panel-position", + "dxo-show": "dxo-load-panel-show", + "dxo-to": "dxo-load-panel-to" + }, + "DxLookupComponent": { + "_hostSelector": "dx-lookup", + "dxi-item": "dxi-lookup-item", + "dxi-toolbar-item": "dxi-lookup-toolbar-item", + "dxo-animation": "dxo-lookup-animation", + "dxo-at": "dxo-lookup-at", + "dxo-boundary-offset": "dxo-lookup-boundary-offset", + "dxo-collision": "dxo-lookup-collision", + "dxo-drop-down-options": "dxo-lookup-drop-down-options", + "dxo-from": "dxo-lookup-from", + "dxo-hide": "dxo-lookup-hide", + "dxo-hide-event": "dxo-lookup-hide-event", + "dxo-my": "dxo-lookup-my", + "dxo-offset": "dxo-lookup-offset", + "dxo-position": "dxo-lookup-position", + "dxo-show": "dxo-lookup-show", + "dxo-show-event": "dxo-lookup-show-event", + "dxo-to": "dxo-lookup-to" + }, + "DxMapComponent": { + "_hostSelector": "dx-map", + "dxi-location": "dxi-map-location", + "dxi-marker": "dxi-map-marker", + "dxi-route": "dxi-map-route", + "dxo-api-key": "dxo-map-api-key", + "dxo-provider-config": "dxo-map-provider-config", + "dxo-tooltip": "dxo-map-tooltip" + }, + "DxMenuComponent": { + "_hostSelector": "dx-menu", + "dxi-item": "dxi-menu-item", + "dxo-animation": "dxo-menu-animation", + "dxo-at": "dxo-menu-at", + "dxo-boundary-offset": "dxo-menu-boundary-offset", + "dxo-collision": "dxo-menu-collision", + "dxo-delay": "dxo-menu-delay", + "dxo-from": "dxo-menu-from", + "dxo-hide": "dxo-menu-hide", + "dxo-my": "dxo-menu-my", + "dxo-offset": "dxo-menu-offset", + "dxo-position": "dxo-menu-position", + "dxo-show": "dxo-menu-show", + "dxo-show-first-submenu-mode": "dxo-menu-show-first-submenu-mode", + "dxo-show-submenu-mode": "dxo-menu-show-submenu-mode", + "dxo-to": "dxo-menu-to" + }, + "DxMultiViewComponent": { + "_hostSelector": "dx-multi-view", + "dxi-item": "dxi-multi-view-item" + }, + "DxNumberBoxComponent": { + "_hostSelector": "dx-number-box", + "dxi-button": "dxi-number-box-button", + "dxo-format": "dxo-number-box-format", + "dxo-options": "dxo-number-box-options" + }, + "DxPieChartComponent": { + "_hostSelector": "dx-pie-chart", + "dxi-annotation": "dxi-pie-chart-annotation", + "dxi-series": "dxi-pie-chart-series", + "dxo-adaptive-layout": "dxo-pie-chart-adaptive-layout", + "dxo-animation": "dxo-pie-chart-animation", + "dxo-argument-format": "dxo-pie-chart-argument-format", + "dxo-border": "dxo-pie-chart-border", + "dxo-color": "dxo-pie-chart-color", + "dxo-common-annotation-settings": "dxo-pie-chart-common-annotation-settings", + "dxo-common-series-settings": "dxo-pie-chart-common-series-settings", + "dxo-connector": "dxo-pie-chart-connector", + "dxo-export": "dxo-pie-chart-export", + "dxo-font": "dxo-pie-chart-font", + "dxo-format": "dxo-pie-chart-format", + "dxo-hatching": "dxo-pie-chart-hatching", + "dxo-hover-style": "dxo-pie-chart-hover-style", + "dxo-image": "dxo-pie-chart-image", + "dxo-label": "dxo-pie-chart-label", + "dxo-legend": "dxo-pie-chart-legend", + "dxo-loading-indicator": "dxo-pie-chart-loading-indicator", + "dxo-margin": "dxo-pie-chart-margin", + "dxo-selection-style": "dxo-pie-chart-selection-style", + "dxo-series-template": "dxo-pie-chart-series-template", + "dxo-shadow": "dxo-pie-chart-shadow", + "dxo-size": "dxo-pie-chart-size", + "dxo-small-values-grouping": "dxo-pie-chart-small-values-grouping", + "dxo-subtitle": "dxo-pie-chart-subtitle", + "dxo-title": "dxo-pie-chart-title", + "dxo-tooltip": "dxo-pie-chart-tooltip" + }, + "DxPivotGridComponent": { + "_hostSelector": "dx-pivot-grid", + "dxo-export": "dxo-pivot-grid-export", + "dxo-field-chooser": "dxo-pivot-grid-field-chooser", + "dxo-field-panel": "dxo-pivot-grid-field-panel", + "dxo-header-filter": "dxo-pivot-grid-header-filter", + "dxo-load-panel": "dxo-pivot-grid-load-panel", + "dxo-scrolling": "dxo-pivot-grid-scrolling", + "dxo-search": "dxo-pivot-grid-search", + "dxo-state-storing": "dxo-pivot-grid-state-storing", + "dxo-texts": "dxo-pivot-grid-texts" + }, + "DxPivotGridFieldChooserComponent": { + "_hostSelector": "dx-pivot-grid-field-chooser", + "dxo-header-filter": "dxo-pivot-grid-field-chooser-header-filter", + "dxo-search": "dxo-pivot-grid-field-chooser-search", + "dxo-texts": "dxo-pivot-grid-field-chooser-texts" + }, + "DxPolarChartComponent": { + "_hostSelector": "dx-polar-chart", + "dxi-annotation": "dxi-polar-chart-annotation", + "dxi-constant-line": "dxi-polar-chart-constant-line", + "dxi-series": "dxi-polar-chart-series", + "dxi-strip": "dxi-polar-chart-strip", + "dxo-adaptive-layout": "dxo-polar-chart-adaptive-layout", + "dxo-animation": "dxo-polar-chart-animation", + "dxo-argument-axis": "dxo-polar-chart-argument-axis", + "dxo-argument-format": "dxo-polar-chart-argument-format", + "dxo-border": "dxo-polar-chart-border", + "dxo-color": "dxo-polar-chart-color", + "dxo-common-annotation-settings": "dxo-polar-chart-common-annotation-settings", + "dxo-common-axis-settings": "dxo-polar-chart-common-axis-settings", + "dxo-common-series-settings": "dxo-polar-chart-common-series-settings", + "dxo-connector": "dxo-polar-chart-connector", + "dxo-constant-line-style": "dxo-polar-chart-constant-line-style", + "dxo-data-prepare-settings": "dxo-polar-chart-data-prepare-settings", + "dxo-export": "dxo-polar-chart-export", + "dxo-font": "dxo-polar-chart-font", + "dxo-format": "dxo-polar-chart-format", + "dxo-grid": "dxo-polar-chart-grid", + "dxo-hatching": "dxo-polar-chart-hatching", + "dxo-hover-style": "dxo-polar-chart-hover-style", + "dxo-image": "dxo-polar-chart-image", + "dxo-label": "dxo-polar-chart-label", + "dxo-legend": "dxo-polar-chart-legend", + "dxo-loading-indicator": "dxo-polar-chart-loading-indicator", + "dxo-margin": "dxo-polar-chart-margin", + "dxo-min-visual-range-length": "dxo-polar-chart-min-visual-range-length", + "dxo-minor-grid": "dxo-polar-chart-minor-grid", + "dxo-minor-tick": "dxo-polar-chart-minor-tick", + "dxo-minor-tick-interval": "dxo-polar-chart-minor-tick-interval", + "dxo-point": "dxo-polar-chart-point", + "dxo-selection-style": "dxo-polar-chart-selection-style", + "dxo-series-template": "dxo-polar-chart-series-template", + "dxo-shadow": "dxo-polar-chart-shadow", + "dxo-size": "dxo-polar-chart-size", + "dxo-strip-style": "dxo-polar-chart-strip-style", + "dxo-subtitle": "dxo-polar-chart-subtitle", + "dxo-tick": "dxo-polar-chart-tick", + "dxo-tick-interval": "dxo-polar-chart-tick-interval", + "dxo-title": "dxo-polar-chart-title", + "dxo-tooltip": "dxo-polar-chart-tooltip", + "dxo-value-axis": "dxo-polar-chart-value-axis", + "dxo-value-error-bar": "dxo-polar-chart-value-error-bar" + }, + "DxPopoverComponent": { + "_hostSelector": "dx-popover", + "dxi-toolbar-item": "dxi-popover-toolbar-item", + "dxo-animation": "dxo-popover-animation", + "dxo-at": "dxo-popover-at", + "dxo-boundary-offset": "dxo-popover-boundary-offset", + "dxo-collision": "dxo-popover-collision", + "dxo-from": "dxo-popover-from", + "dxo-hide": "dxo-popover-hide", + "dxo-hide-event": "dxo-popover-hide-event", + "dxo-my": "dxo-popover-my", + "dxo-offset": "dxo-popover-offset", + "dxo-position": "dxo-popover-position", + "dxo-show": "dxo-popover-show", + "dxo-show-event": "dxo-popover-show-event", + "dxo-to": "dxo-popover-to" + }, + "DxRadioGroupComponent": { + "_hostSelector": "dx-radio-group", + "dxi-item": "dxi-radio-group-item" + }, + "DxRangeSelectorComponent": { + "_hostSelector": "dx-range-selector", + "dxi-break": "dxi-range-selector-break", + "dxi-series": "dxi-range-selector-series", + "dxo-aggregation": "dxo-range-selector-aggregation", + "dxo-aggregation-interval": "dxo-range-selector-aggregation-interval", + "dxo-argument-format": "dxo-range-selector-argument-format", + "dxo-background": "dxo-range-selector-background", + "dxo-behavior": "dxo-range-selector-behavior", + "dxo-border": "dxo-range-selector-border", + "dxo-break-style": "dxo-range-selector-break-style", + "dxo-chart": "dxo-range-selector-chart", + "dxo-color": "dxo-range-selector-color", + "dxo-common-series-settings": "dxo-range-selector-common-series-settings", + "dxo-connector": "dxo-range-selector-connector", + "dxo-data-prepare-settings": "dxo-range-selector-data-prepare-settings", + "dxo-export": "dxo-range-selector-export", + "dxo-font": "dxo-range-selector-font", + "dxo-format": "dxo-range-selector-format", + "dxo-hatching": "dxo-range-selector-hatching", + "dxo-height": "dxo-range-selector-height", + "dxo-hover-style": "dxo-range-selector-hover-style", + "dxo-image": "dxo-range-selector-image", + "dxo-indent": "dxo-range-selector-indent", + "dxo-label": "dxo-range-selector-label", + "dxo-loading-indicator": "dxo-range-selector-loading-indicator", + "dxo-margin": "dxo-range-selector-margin", + "dxo-marker": "dxo-range-selector-marker", + "dxo-max-range": "dxo-range-selector-max-range", + "dxo-min-range": "dxo-range-selector-min-range", + "dxo-minor-tick": "dxo-range-selector-minor-tick", + "dxo-minor-tick-interval": "dxo-range-selector-minor-tick-interval", + "dxo-point": "dxo-range-selector-point", + "dxo-reduction": "dxo-range-selector-reduction", + "dxo-scale": "dxo-range-selector-scale", + "dxo-selection-style": "dxo-range-selector-selection-style", + "dxo-series-template": "dxo-range-selector-series-template", + "dxo-shutter": "dxo-range-selector-shutter", + "dxo-size": "dxo-range-selector-size", + "dxo-slider-handle": "dxo-range-selector-slider-handle", + "dxo-slider-marker": "dxo-range-selector-slider-marker", + "dxo-subtitle": "dxo-range-selector-subtitle", + "dxo-tick": "dxo-range-selector-tick", + "dxo-tick-interval": "dxo-range-selector-tick-interval", + "dxo-title": "dxo-range-selector-title", + "dxo-url": "dxo-range-selector-url", + "dxo-value-axis": "dxo-range-selector-value-axis", + "dxo-value-error-bar": "dxo-range-selector-value-error-bar", + "dxo-width": "dxo-range-selector-width" + }, + "DxRangeSliderComponent": { + "_hostSelector": "dx-range-slider", + "dxo-format": "dxo-range-slider-format", + "dxo-label": "dxo-range-slider-label", + "dxo-tooltip": "dxo-range-slider-tooltip" + }, + "DxResponsiveBoxComponent": { + "_hostSelector": "dx-responsive-box", + "dxi-col": "dxi-responsive-box-col", + "dxi-item": "dxi-responsive-box-item", + "dxi-location": "dxi-responsive-box-location", + "dxi-row": "dxi-responsive-box-row" + }, + "DxSankeyComponent": { + "_hostSelector": "dx-sankey", + "dxo-adaptive-layout": "dxo-sankey-adaptive-layout", + "dxo-border": "dxo-sankey-border", + "dxo-export": "dxo-sankey-export", + "dxo-font": "dxo-sankey-font", + "dxo-format": "dxo-sankey-format", + "dxo-hatching": "dxo-sankey-hatching", + "dxo-hover-style": "dxo-sankey-hover-style", + "dxo-label": "dxo-sankey-label", + "dxo-link": "dxo-sankey-link", + "dxo-loading-indicator": "dxo-sankey-loading-indicator", + "dxo-margin": "dxo-sankey-margin", + "dxo-node": "dxo-sankey-node", + "dxo-shadow": "dxo-sankey-shadow", + "dxo-size": "dxo-sankey-size", + "dxo-subtitle": "dxo-sankey-subtitle", + "dxo-title": "dxo-sankey-title", + "dxo-tooltip": "dxo-sankey-tooltip" + }, + "DxSchedulerComponent": { + "_hostSelector": "dx-scheduler", + "dxi-item": "dxi-scheduler-item", + "dxi-resource": "dxi-scheduler-resource", + "dxi-toolbar-item": "dxi-scheduler-toolbar-item", + "dxi-view": "dxi-scheduler-view", + "dxo-appointment-dragging": "dxo-scheduler-appointment-dragging", + "dxo-editing": "dxo-scheduler-editing", + "dxo-options": "dxo-scheduler-options", + "dxo-scrolling": "dxo-scheduler-scrolling", + "dxo-toolbar": "dxo-scheduler-toolbar" + }, + "DxSelectBoxComponent": { + "_hostSelector": "dx-select-box", + "dxi-button": "dxi-select-box-button", + "dxi-item": "dxi-select-box-item", + "dxi-toolbar-item": "dxi-select-box-toolbar-item", + "dxo-animation": "dxo-select-box-animation", + "dxo-at": "dxo-select-box-at", + "dxo-boundary-offset": "dxo-select-box-boundary-offset", + "dxo-collision": "dxo-select-box-collision", + "dxo-drop-down-options": "dxo-select-box-drop-down-options", + "dxo-from": "dxo-select-box-from", + "dxo-hide": "dxo-select-box-hide", + "dxo-my": "dxo-select-box-my", + "dxo-offset": "dxo-select-box-offset", + "dxo-options": "dxo-select-box-options", + "dxo-position": "dxo-select-box-position", + "dxo-show": "dxo-select-box-show", + "dxo-to": "dxo-select-box-to" + }, + "DxSliderComponent": { + "_hostSelector": "dx-slider", + "dxo-format": "dxo-slider-format", + "dxo-label": "dxo-slider-label", + "dxo-tooltip": "dxo-slider-tooltip" + }, + "DxSortableComponent": { + "_hostSelector": "dx-sortable", + "dxo-cursor-offset": "dxo-sortable-cursor-offset" + }, + "DxSparklineComponent": { + "_hostSelector": "dx-sparkline", + "dxo-border": "dxo-sparkline-border", + "dxo-font": "dxo-sparkline-font", + "dxo-format": "dxo-sparkline-format", + "dxo-margin": "dxo-sparkline-margin", + "dxo-shadow": "dxo-sparkline-shadow", + "dxo-size": "dxo-sparkline-size", + "dxo-tooltip": "dxo-sparkline-tooltip" + }, + "DxSplitterComponent": { + "_hostSelector": "dx-splitter", + "dxi-item": "dxi-splitter-item" + }, + "DxStepperComponent": { + "_hostSelector": "dx-stepper", + "dxi-item": "dxi-stepper-item" + }, + "DxTabPanelComponent": { + "_hostSelector": "dx-tab-panel", + "dxi-item": "dxi-tab-panel-item" + }, + "DxTabsComponent": { + "_hostSelector": "dx-tabs", + "dxi-item": "dxi-tabs-item" + }, + "DxTagBoxComponent": { + "_hostSelector": "dx-tag-box", + "dxi-button": "dxi-tag-box-button", + "dxi-item": "dxi-tag-box-item", + "dxi-toolbar-item": "dxi-tag-box-toolbar-item", + "dxo-animation": "dxo-tag-box-animation", + "dxo-at": "dxo-tag-box-at", + "dxo-boundary-offset": "dxo-tag-box-boundary-offset", + "dxo-collision": "dxo-tag-box-collision", + "dxo-drop-down-options": "dxo-tag-box-drop-down-options", + "dxo-from": "dxo-tag-box-from", + "dxo-hide": "dxo-tag-box-hide", + "dxo-my": "dxo-tag-box-my", + "dxo-offset": "dxo-tag-box-offset", + "dxo-options": "dxo-tag-box-options", + "dxo-position": "dxo-tag-box-position", + "dxo-show": "dxo-tag-box-show", + "dxo-to": "dxo-tag-box-to" + }, + "DxTextBoxComponent": { + "_hostSelector": "dx-text-box", + "dxi-button": "dxi-text-box-button", + "dxo-options": "dxo-text-box-options" + }, + "DxTileViewComponent": { + "_hostSelector": "dx-tile-view", + "dxi-item": "dxi-tile-view-item" + }, + "DxToastComponent": { + "_hostSelector": "dx-toast", + "dxo-animation": "dxo-toast-animation", + "dxo-at": "dxo-toast-at", + "dxo-boundary-offset": "dxo-toast-boundary-offset", + "dxo-collision": "dxo-toast-collision", + "dxo-from": "dxo-toast-from", + "dxo-hide": "dxo-toast-hide", + "dxo-my": "dxo-toast-my", + "dxo-offset": "dxo-toast-offset", + "dxo-position": "dxo-toast-position", + "dxo-show": "dxo-toast-show", + "dxo-to": "dxo-toast-to" + }, + "DxToolbarComponent": { + "_hostSelector": "dx-toolbar", + "dxi-item": "dxi-toolbar-item" + }, + "DxTooltipComponent": { + "_hostSelector": "dx-tooltip", + "dxo-animation": "dxo-tooltip-animation", + "dxo-at": "dxo-tooltip-at", + "dxo-boundary-offset": "dxo-tooltip-boundary-offset", + "dxo-collision": "dxo-tooltip-collision", + "dxo-from": "dxo-tooltip-from", + "dxo-hide": "dxo-tooltip-hide", + "dxo-hide-event": "dxo-tooltip-hide-event", + "dxo-my": "dxo-tooltip-my", + "dxo-offset": "dxo-tooltip-offset", + "dxo-position": "dxo-tooltip-position", + "dxo-show": "dxo-tooltip-show", + "dxo-show-event": "dxo-tooltip-show-event", + "dxo-to": "dxo-tooltip-to" + }, + "DxTreeListComponent": { + "_hostSelector": "dx-tree-list", + "dxi-button": "dxi-tree-list-button", + "dxi-change": "dxi-tree-list-change", + "dxi-column": "dxi-tree-list-column", + "dxi-custom-operation": "dxi-tree-list-custom-operation", + "dxi-field": "dxi-tree-list-field", + "dxi-item": "dxi-tree-list-item", + "dxi-toolbar-item": "dxi-tree-list-toolbar-item", + "dxi-validation-rule": "dxi-tree-list-validation-rule", + "dxo-animation": "dxo-tree-list-animation", + "dxo-at": "dxo-tree-list-at", + "dxo-boundary-offset": "dxo-tree-list-boundary-offset", + "dxo-col-count-by-screen": "dxo-tree-list-col-count-by-screen", + "dxo-collision": "dxo-tree-list-collision", + "dxo-column-chooser": "dxo-tree-list-column-chooser", + "dxo-column-fixing": "dxo-tree-list-column-fixing", + "dxo-cursor-offset": "dxo-tree-list-cursor-offset", + "dxo-editing": "dxo-tree-list-editing", + "dxo-filter-builder": "dxo-tree-list-filter-builder", + "dxo-filter-builder-popup": "dxo-tree-list-filter-builder-popup", + "dxo-filter-operation-descriptions": "dxo-tree-list-filter-operation-descriptions", + "dxo-filter-panel": "dxo-tree-list-filter-panel", + "dxo-filter-row": "dxo-tree-list-filter-row", + "dxo-form": "dxo-tree-list-form", + "dxo-form-item": "dxo-tree-list-form-item", + "dxo-format": "dxo-tree-list-format", + "dxo-from": "dxo-tree-list-from", + "dxo-group-operation-descriptions": "dxo-tree-list-group-operation-descriptions", + "dxo-header-filter": "dxo-tree-list-header-filter", + "dxo-hide": "dxo-tree-list-hide", + "dxo-icons": "dxo-tree-list-icons", + "dxo-keyboard-navigation": "dxo-tree-list-keyboard-navigation", + "dxo-label": "dxo-tree-list-label", + "dxo-load-panel": "dxo-tree-list-load-panel", + "dxo-lookup": "dxo-tree-list-lookup", + "dxo-my": "dxo-tree-list-my", + "dxo-offset": "dxo-tree-list-offset", + "dxo-operation-descriptions": "dxo-tree-list-operation-descriptions", + "dxo-pager": "dxo-tree-list-pager", + "dxo-paging": "dxo-tree-list-paging", + "dxo-popup": "dxo-tree-list-popup", + "dxo-position": "dxo-tree-list-position", + "dxo-remote-operations": "dxo-tree-list-remote-operations", + "dxo-row-dragging": "dxo-tree-list-row-dragging", + "dxo-scrolling": "dxo-tree-list-scrolling", + "dxo-search": "dxo-tree-list-search", + "dxo-search-panel": "dxo-tree-list-search-panel", + "dxo-selection": "dxo-tree-list-selection", + "dxo-show": "dxo-tree-list-show", + "dxo-sorting": "dxo-tree-list-sorting", + "dxo-state-storing": "dxo-tree-list-state-storing", + "dxo-texts": "dxo-tree-list-texts", + "dxo-to": "dxo-tree-list-to", + "dxo-toolbar": "dxo-tree-list-toolbar" + }, + "DxTreeMapComponent": { + "_hostSelector": "dx-tree-map", + "dxo-border": "dxo-tree-map-border", + "dxo-colorizer": "dxo-tree-map-colorizer", + "dxo-export": "dxo-tree-map-export", + "dxo-font": "dxo-tree-map-font", + "dxo-format": "dxo-tree-map-format", + "dxo-group": "dxo-tree-map-group", + "dxo-hover-style": "dxo-tree-map-hover-style", + "dxo-label": "dxo-tree-map-label", + "dxo-loading-indicator": "dxo-tree-map-loading-indicator", + "dxo-margin": "dxo-tree-map-margin", + "dxo-selection-style": "dxo-tree-map-selection-style", + "dxo-shadow": "dxo-tree-map-shadow", + "dxo-size": "dxo-tree-map-size", + "dxo-subtitle": "dxo-tree-map-subtitle", + "dxo-tile": "dxo-tree-map-tile", + "dxo-title": "dxo-tree-map-title", + "dxo-tooltip": "dxo-tree-map-tooltip" + }, + "DxTreeViewComponent": { + "_hostSelector": "dx-tree-view", + "dxi-button": "dxi-tree-view-button", + "dxi-item": "dxi-tree-view-item", + "dxo-options": "dxo-tree-view-options", + "dxo-search-editor-options": "dxo-tree-view-search-editor-options" + }, + "DxValidationSummaryComponent": { + "_hostSelector": "dx-validation-summary", + "dxi-item": "dxi-validation-summary-item" + }, + "DxValidatorComponent": { + "_hostSelector": "dx-validator", + "dxi-validation-rule": "dxi-validator-validation-rule", + "dxo-adapter": "dxo-validator-adapter" + }, + "DxVectorMapComponent": { + "_hostSelector": "dx-vector-map", + "dxi-annotation": "dxi-vector-map-annotation", + "dxi-layer": "dxi-vector-map-layer", + "dxi-legend": "dxi-vector-map-legend", + "dxo-background": "dxo-vector-map-background", + "dxo-border": "dxo-vector-map-border", + "dxo-common-annotation-settings": "dxo-vector-map-common-annotation-settings", + "dxo-control-bar": "dxo-vector-map-control-bar", + "dxo-export": "dxo-vector-map-export", + "dxo-font": "dxo-vector-map-font", + "dxo-image": "dxo-vector-map-image", + "dxo-label": "dxo-vector-map-label", + "dxo-loading-indicator": "dxo-vector-map-loading-indicator", + "dxo-margin": "dxo-vector-map-margin", + "dxo-projection": "dxo-vector-map-projection", + "dxo-shadow": "dxo-vector-map-shadow", + "dxo-size": "dxo-vector-map-size", + "dxo-source": "dxo-vector-map-source", + "dxo-subtitle": "dxo-vector-map-subtitle", + "dxo-title": "dxo-vector-map-title", + "dxo-tooltip": "dxo-vector-map-tooltip" + } +} diff --git a/packages/devextreme-schematics/src/migrate-nested-components/schema.json b/packages/devextreme-schematics/src/migrate-nested-components/schema.json new file mode 100644 index 000000000..88ce925b3 --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-nested-components/schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "SchematicsDevextremeMigrateNestedComponents", + "title": "Migrate deprecated nested components to new ones", + "type": "object", + "properties": { + "include": { + "type": "array", + "items": { "type": "string" }, + "description": "Glob(s) of template files to include (default: **/*.html)" + }, + "scriptInclude": { + "type": "array", + "items": { "type": "string" }, + "description": "Glob(s) for TS/JS files to scan for inline @Component({ template }) (default: ['**/*.ts','**/*.js']; set to [] to disable)" + }, + "dry": { "type": "boolean", "default": false } + } +} \ No newline at end of file diff --git a/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts b/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts new file mode 100644 index 000000000..2f7bab827 --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts @@ -0,0 +1,311 @@ +import { Tree } from '@angular-devkit/schematics'; +import * as parse5 from 'parse5'; + +// Dynamically require TypeScript if available; skip inline template migration if not. +let ts: any; +try { + // tslint:disable-next-line:no-var-requires + ts = require('typescript'); +} catch { + ts = null; // Will be checked before inline processing +} + +// Ensure picomatch is loaded as a callable matcher factory in all module systems +// tslint:disable-next-line:no-var-requires +const picomatch: any = require('picomatch'); + +// Minimal parse5 types for our usage +interface P5Node { [k: string]: any; } +interface P5Element extends P5Node { tagName?: string; } +interface P5Document extends P5Node { + documentElement?: P5Element; +} + +export interface HostRule { + hostSelector: string; + nestedMap: Record; +} + +export interface RunnerOptions { + includeGlobs: string[]; + rules: HostRule[]; +} + +export interface ExecOptions { + dryRun: boolean; + logger: { info: (s: string) => void; warn: (s: string) => void }; +} + +// Transform external HTML template files matched by includeGlobs. +export async function applyHostAwareTemplateMigrations( + tree: Tree, + runner: RunnerOptions, + exec: ExecOptions +): Promise { + const matcher = picomatch(runner.includeGlobs.length ? runner.includeGlobs : ['**/*.html']); + + tree.visit(filePath => { + if (!matcher(filePath)) { + return; + } + if (!filePath.endsWith('.html')) { + return; + } + + const buffer = tree.read(filePath); + if (!buffer) { + return; + } + + const source = buffer.toString('utf8'); + const { updated, changeCount } = transformTemplate(source, runner.rules); + if (changeCount === 0) { + return; + } + if (exec.dryRun) { + exec.logger.info(`[dry] ${filePath} → ${changeCount} changes`); + } else { + exec.logger.info(`${filePath} → ${changeCount} changes`); + tree.overwrite(filePath, updated); + } + }); +} + +// Apply migrations inside inline component templates found in TS/JS files. +export async function applyInlineComponentTemplateMigrations( + tree: Tree, + runner: RunnerOptions, + exec: ExecOptions, + scriptGlobs: string[] +): Promise { + if (!scriptGlobs.length) { + return; + } + if (!ts) { + exec.logger.warn( + '[nested-migrator] Skipping inline template migration (TypeScript not available). ' + + 'Install "typescript" if you want inline template support.'); + return; + } + const matcher = picomatch(scriptGlobs); + tree.visit(filePath => { + if (!matcher(filePath)) { + return; + } + if (!(filePath.endsWith('.ts') || filePath.endsWith('.js'))) { + return; + } + + const buffer = tree.read(filePath); + if (!buffer) { + return; + } + const sourceText = buffer.toString('utf8'); + + const sf = ts.createSourceFile( + filePath, sourceText, ts.ScriptTarget.ES2022, true, + filePath.endsWith('.ts') ? ts.ScriptKind.TS : ts.ScriptKind.JS); + + interface TemplateEdit { start: number; end: number; text: string; changes: number; } + const edits: TemplateEdit[] = []; + + function visit(node: any) { + if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) { + const call = node.expression; + if (ts.isIdentifier(call.expression) && call.expression.text === 'Component' && call.arguments.length) { + const arg = call.arguments[0]; + if (ts.isObjectLiteralExpression(arg)) { + for (const prop of arg.properties) { + if (!ts.isPropertyAssignment(prop)) { + continue; + } + const name = prop.name; + const propName = ts.isIdentifier(name) ? name.text : ts.isStringLiteral(name) ? name.text : undefined; + if (propName !== 'template') { + continue; + } + const init = prop.initializer; + if (ts.isStringLiteral(init)) { + const raw = init.text; + const { updated, changeCount } = transformTemplate(raw, runner.rules); + if (changeCount > 0) { + const quote = init.getText().startsWith("'") ? '"' : init.getText()[0]; + const newLiteral = quote + updated.replace(new RegExp(quote, 'g'), '\\' + quote) + quote; + edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount }); + } + } else if (ts.isNoSubstitutionTemplateLiteral(init)) { + const raw = init.text; + const { updated, changeCount } = transformTemplate(raw, runner.rules); + if (changeCount > 0) { + const newLiteral = '`' + escapeBackticks(updated) + '`'; + edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount }); + } + } else if (ts.isTemplateExpression(init)) { + const { placeholderContent, placeholders } = flattenTemplateExpression(init, sourceText); + const { updated, changeCount } = transformTemplate(placeholderContent, runner.rules); + if (changeCount > 0) { + let rebuilt = updated; + placeholders.forEach(placeholder => { + rebuilt = rebuilt.replace(new RegExp(placeholder.token, 'g'), placeholder.fullText); + }); + const newLiteral = '`' + escapeBackticks(rebuilt) + '`'; + edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount }); + } + } + } + } + } + } + ts.forEachChild(node, visit); + } + visit(sf); + + if (!edits.length) { + return; + } + edits.sort((a, b) => b.start - a.start); // edits from last to first + let updatedFile = sourceText; + for (const edit of edits) { + updatedFile = updatedFile.slice(0, edit.start) + edit.text + updatedFile.slice(edit.end); + } + const totalChanges = edits.reduce((acc, edit) => acc + edit.changes, 0); + if (exec.dryRun) { + exec.logger.info( + `[dry] ${filePath} (inline templates) → ${totalChanges} changes in ${edits.length} template(s)`); + } else { + exec.logger.info( + `${filePath} (inline templates) → ${totalChanges} changes in ${edits.length} template(s)`); + tree.overwrite(filePath, updatedFile); + } + }); +} + +export function transformTemplate( + source: string, + rules: RunnerOptions['rules'] +): { updated: string; changeCount: number } { + const document = parse5.parse(source, { sourceCodeLocationInfo: true }) as unknown as P5Document; + const replacements: Array<{ start: number; end: number; text: string }> = []; + + const hostSelectorSet = new Set(rules.map(rule => rule.hostSelector)); + + const seenOpens = new Set(); + const seenCloses = new Set(); + + function walkHost(root: P5Element, visitFn: (node: P5Node) => void) { + function recursiveWalk(node: P5Node) { + visitFn(node); + const childNodes = (node as any).childNodes as P5Node[] | undefined; + if (!childNodes) { + return; + } + for (const childNode of childNodes) { + if (isElement(childNode) && hostSelectorSet.has((childNode as any).tagName) && childNode !== root) { + continue; + } + recursiveWalk(childNode); + } + } + recursiveWalk(root); + } + + for (const rule of rules) { + const hosts = findElementsByTag(document, rule.hostSelector); + for (const host of hosts) { + if (!(host as any).sourceCodeLocation) { + continue; + } + walkHost(host, (node) => { + if (!isElement(node)) { + return; + } + const oldName: string | undefined = (node as any).tagName; + if (!oldName) { + return; + } + const newName = rule.nestedMap[oldName]; + if (!newName) { + return; + } + const loc: any = (node as any).sourceCodeLocation; + if (!loc) { + return; + } + if (loc.startTag) { + const openStart = loc.startTag.startOffset + 1; + const openEnd = openStart + oldName.length; + if (!seenOpens.has(openStart)) { + seenOpens.add(openStart); + replacements.push({ start: openStart, end: openEnd, text: newName }); + } + } + if (loc.endTag) { + const endStart = loc.endTag.startOffset + 2; + const endEnd = endStart + oldName.length; + if (!seenCloses.has(endStart)) { + seenCloses.add(endStart); + replacements.push({ start: endStart, end: endEnd, text: newName }); + } + } + }); + } + } + + if (!replacements.length) { + return { updated: source, changeCount: 0 }; + } + replacements.sort((a, b) => b.start - a.start); + let updated = source; + for (const replacement of replacements) { + updated = updated.slice(0, replacement.start) + replacement.text + updated.slice(replacement.end); + } + return { updated, changeCount: replacements.length }; +} + +// Helpers + +function flattenTemplateExpression( + node: any, + source: string +): { placeholderContent: string; placeholders: Array<{ token: string; fullText: string }> } { + const placeholders: Array<{ token: string; fullText: string }> = []; + let content = node.head.text; + node.templateSpans.forEach((span: any, i: number) => { + const token = `__NG_EXPR_PLACEHOLDER_${i}__`; + const fullText = '${' + source.slice(span.expression.getStart(), span.expression.getEnd()) + '}'; + placeholders.push({ token, fullText }); + content += token + span.literal.text; + }); + return { placeholderContent: content, placeholders }; +} + +function escapeBackticks(text: string): string { + return text.replace(/`/g, '\\`'); +} + +// Utilities + +function isElement(n: P5Node): n is P5Element { + return (n as any).tagName !== undefined; +} + +function walk(node: P5Node, visit: (n: P5Node) => void) { + visit(node); + const childNodes = (node as any).childNodes as P5Node[] | undefined; + if (!childNodes) { + return; + } + for (const childNode of childNodes) { + walk(childNode, visit); + } +} + +function findElementsByTag(doc: P5Document | P5Element, tag: string): P5Element[] { + const result: P5Element[] = []; + walk(doc as unknown as P5Node, (node) => { + if (isElement(node) && node.tagName === tag) { + result.push(node); + } + }); + return result; +} diff --git a/packages/devextreme-schematics/tsconfig.json b/packages/devextreme-schematics/tsconfig.json index 0b296985d..a458abe18 100644 --- a/packages/devextreme-schematics/tsconfig.json +++ b/packages/devextreme-schematics/tsconfig.json @@ -8,6 +8,8 @@ "declaration": true, "module": "commonjs", "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitAny": true, From f5b24484d98d30103c0b49143ba92021427f76d9 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 17 Sep 2025 18:44:55 +0400 Subject: [PATCH 2/8] fix proper escaping issue --- .../src/migrate-nested-components/template-migrator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts b/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts index 2f7bab827..b47c635d6 100644 --- a/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts +++ b/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts @@ -280,7 +280,8 @@ function flattenTemplateExpression( } function escapeBackticks(text: string): string { - return text.replace(/`/g, '\\`'); + // First escape backslashes, then escape backticks + return text.replace(/\\/g, '\\\\').replace(/`/g, '\\`'); } // Utilities From 7ac471bbe402d14303fd937240d87eb531cf7d56 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 22 Oct 2025 02:54:43 +0400 Subject: [PATCH 3/8] cli fixes according to the plan --- packages/devextreme-cli/index.js | 8 +- packages/devextreme-cli/src/application.js | 23 ++++-- .../src/applications/application.angular.js | 6 +- packages/devextreme-cli/src/commands.json | 28 ++++--- packages/devextreme-schematics/README.md | 27 +++++++ .../devextreme-schematics/package-lock.json | 8 ++ packages/devextreme-schematics/package.json | 1 + .../devextreme-schematics/src/collection.json | 8 +- .../README.md | 30 ++++--- .../src/migrate-config-components/index.ts | 79 +++++++++++++++++++ .../mappings/deprecated-config-map.json} | 4 +- .../src/migrate-config-components/schema.json | 28 +++++++ .../template-migrator.ts | 55 +++++++++---- .../src/migrate-nested-components/index.ts | 48 ----------- .../src/migrate-nested-components/schema.json | 19 ----- 15 files changed, 249 insertions(+), 123 deletions(-) rename packages/devextreme-schematics/src/{migrate-nested-components => migrate-config-components}/README.md (62%) create mode 100644 packages/devextreme-schematics/src/migrate-config-components/index.ts rename packages/devextreme-schematics/src/{migrate-nested-components/mappings/deprecated-nested-map.json => migrate-config-components/mappings/deprecated-config-map.json} (99%) create mode 100644 packages/devextreme-schematics/src/migrate-config-components/schema.json rename packages/devextreme-schematics/src/{migrate-nested-components => migrate-config-components}/template-migrator.ts (86%) delete mode 100644 packages/devextreme-schematics/src/migrate-nested-components/index.ts delete mode 100644 packages/devextreme-schematics/src/migrate-nested-components/schema.json diff --git a/packages/devextreme-cli/index.js b/packages/devextreme-cli/index.js index f0957ca67..94d2a68a5 100644 --- a/packages/devextreme-cli/index.js +++ b/packages/devextreme-cli/index.js @@ -40,7 +40,13 @@ const run = async(commands, options) => { if(application.isApplicationCommand(commands[0])) { await application.run(commands, options, devextremeConfig.read()); } else if(application.isMigrationCommand(commands[0])) { - await application.run(commands, options, { applicationEngine: 'angular' }); + if(!commands[1]) { + console.error('Please specify a change name for migration.'); + printHelp('migrate'); + return; + } + await application.run(['migrate', commands[1], ...commands.slice(2)], options, { applicationEngine: 'angular' }); + return; } else if(themeBuilder.isThemeBuilderCommand(commands[0])) { options.command = commands[0]; themeBuilder.run(options); diff --git a/packages/devextreme-cli/src/application.js b/packages/devextreme-cli/src/application.js index bc4c7f291..c931ad27e 100644 --- a/packages/devextreme-cli/src/application.js +++ b/packages/devextreme-cli/src/application.js @@ -10,7 +10,7 @@ const isApplicationCommand = (command) => { }; const isMigrationCommand = (command) => { - return command === 'migrate-nested-components'; + return [ 'migrate' ].includes(command); }; const handleWrongAppType = (appType, command) => { @@ -18,6 +18,11 @@ const handleWrongAppType = (appType, command) => { printHelp(command); }; +const handleWrongChangeName = (changeName, command) => { + console.error(`The '${changeName}' change name is not valid`); + printHelp(command); +}; + const createReact = async(appName, options, command) => { const reactAppType = await getReactAppType(options['app-type']); @@ -34,10 +39,6 @@ const createReact = async(appName, options, command) => { }; const run = async(commands, options, devextremeConfig) => { - if(commands[0] === 'migrate-nested-components') { - await angularApplication.migrateNestedComponents(options); - return; - } if(!commands[1]) { console.error('Command is incomplete. Please specify parameters.'); @@ -45,6 +46,18 @@ const run = async(commands, options, devextremeConfig) => { return; } + if(commands[0] === 'migrate') { + const changeName = commands[1]; + switch(changeName) { + case 'angular-config-components': + await angularApplication.migrateConfigComponents(options); + return; + default: + handleWrongChangeName(changeName, commands[0]); + return; + } + } + if(commands[0] === 'new') { const app = commands[1]; const appName = commands[2] || 'my-app'; diff --git a/packages/devextreme-cli/src/applications/application.angular.js b/packages/devextreme-cli/src/applications/application.angular.js index c58c49f9c..321dbd22c 100644 --- a/packages/devextreme-cli/src/applications/application.angular.js +++ b/packages/devextreme-cli/src/applications/application.angular.js @@ -163,7 +163,7 @@ const addView = (viewName, options) => { runSchematicCommand('add-view', schematicOptions); }; -const migrateNestedComponents = async(options = {}) => { +const migrateConfigComponents = async(options = {}) => { const collectionName = 'devextreme-schematics'; // Check if devextreme-schematics is installed @@ -207,7 +207,7 @@ const migrateNestedComponents = async(options = {}) => { schematicOptions.scriptInclude = schematicOptions.scriptInclude.split(',').map(s => s.trim()); } - const commandArguments = ['schematics', `${collectionName}:migrate-nested-components`]; + const commandArguments = ['schematics', `${collectionName}:migrate-config-components`]; const { [depsVersionTagOptionName]: _, ...optionsToArguments } = schematicOptions; // eslint-disable-line no-unused-vars for(let option in optionsToArguments) { @@ -250,5 +250,5 @@ module.exports = { create, addTemplate, addView, - migrateNestedComponents + migrateConfigComponents }; diff --git a/packages/devextreme-cli/src/commands.json b/packages/devextreme-cli/src/commands.json index d6c71ff15..43e4321f8 100644 --- a/packages/devextreme-cli/src/commands.json +++ b/packages/devextreme-cli/src/commands.json @@ -49,18 +49,22 @@ "description": "Add DevExtreme to an Angular application" }] }, { - "name": "migrate-nested-components", - "description": "Migrate deprecated nested DevExtreme components to new ones", - "usage": "devextreme migrate-nested-components [options]", - "options": [{ - "name": "--include", - "description": "Glob patterns of template files to include (default: **/*.html). Separate multiple patterns with commas." - }, { - "name": "--script-include", - "description": "Glob patterns for TypeScript/JavaScript files to scan for inline @Component({ template }) (default: **/*.ts,**/*.js). Set to empty to disable." - }, { - "name": "--dry", - "description": "Run in dry mode to preview changes without applying them (default: false)" + "name": "migrate", + "description": "Migration commands for DevExtreme applications", + "usage": "devextreme migrate [options]", + "arguments": [{ + "name": "angular-config-components", + "description": "Migrate DevExtreme configuration components to the latest version.", + "options": [{ + "name": "--include", + "description": "Glob patterns of template files to include (default: **/*.html). You can pass multiple patterns as a comma-separated string (e.g. \"**/a.html,**/b.html\") or as an array (e.g. [\"**/a.html\",\"**/b.html\"])." + }, { + "name": "--script-include", + "description": "Glob patterns for TypeScript/JavaScript files to scan for inline @Component({ template }) (default: **/*.ts,**/*.js). You can pass multiple patterns as a comma-separated string or as an array. Set to empty to disable." + }, { + "name": "--dry", + "description": "Run in dry mode to preview changes without applying them (default: false)" + }] }] }, { "name": "build-theme", diff --git a/packages/devextreme-schematics/README.md b/packages/devextreme-schematics/README.md index 851aaee14..2907f2b92 100644 --- a/packages/devextreme-schematics/README.md +++ b/packages/devextreme-schematics/README.md @@ -19,3 +19,30 @@ This package includes the following schematics: - [add-view](src/add-view) Adds a view to a DevExtreme Angular application + +## TypeScript Dependency & Global CLI Usage + +Some DevExtreme migration schematics require TypeScript to process inline Angular templates. The CLI attempts to resolve TypeScript from multiple locations: + +- The CLI's own node_modules +- Your project's node_modules +- The global node_modules + +If TypeScript is not found, inline template migration will be skipped and a warning will be shown with resolution attempts and errors. + +### How to Fix TypeScript Not Available + +1. **Local Project:** Install TypeScript in your project root: + ```sh + npm install typescript --save-dev + ``` +2. **Global CLI:** If you use the CLI globally, also install TypeScript globally: + ```sh + npm install -g typescript + ``` +3. **npx Usage:** If you use npx, ensure TypeScript is available in your workspace or globally. +4. **Troubleshooting:** + - Some npm global installs may not link dependencies as expected. If you see repeated TypeScript resolution errors, try running the CLI from a project where TypeScript is installed locally. + - You can also manually link TypeScript to your global node_modules if needed. + +If you continue to see errors, review the warning output for resolution attempts and check your npm/node installation paths. diff --git a/packages/devextreme-schematics/package-lock.json b/packages/devextreme-schematics/package-lock.json index a161e9b31..af900b312 100644 --- a/packages/devextreme-schematics/package-lock.json +++ b/packages/devextreme-schematics/package-lock.json @@ -18,6 +18,7 @@ "devDependencies": { "@types/jasmine": "~3.10.18", "@types/node": "ts5.2", + "@types/picomatch": "^4.0.2", "@types/semver": "^7.7.1", "jasmine": "^2.99.0", "rxjs": "^6.6.7", @@ -247,6 +248,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", diff --git a/packages/devextreme-schematics/package.json b/packages/devextreme-schematics/package.json index 036aad279..ca91ca4bf 100644 --- a/packages/devextreme-schematics/package.json +++ b/packages/devextreme-schematics/package.json @@ -31,6 +31,7 @@ "devDependencies": { "@types/jasmine": "~3.10.18", "@types/node": "ts5.2", + "@types/picomatch": "^4.0.2", "@types/semver": "^7.7.1", "jasmine": "^2.99.0", "rxjs": "^6.6.7", diff --git a/packages/devextreme-schematics/src/collection.json b/packages/devextreme-schematics/src/collection.json index 2e5713370..a705828e2 100644 --- a/packages/devextreme-schematics/src/collection.json +++ b/packages/devextreme-schematics/src/collection.json @@ -25,10 +25,10 @@ "factory": "./add-view/index", "schema": "./add-view/schema.json" }, - "migrate-nested-components": { - "description": "Migrate nested DevExtreme components to the latest version.", - "factory": "./migrate-nested-components/index#migrateNestedComponents", - "schema": "./migrate-nested-components/schema.json" + "migrate-config-components": { + "description": "Migrate DevExtreme configurationcomponents to the latest version.", + "factory": "./migrate-config-components/index#migrateConfigComponents", + "schema": "./migrate-config-components/schema.json" } } } diff --git a/packages/devextreme-schematics/src/migrate-nested-components/README.md b/packages/devextreme-schematics/src/migrate-config-components/README.md similarity index 62% rename from packages/devextreme-schematics/src/migrate-nested-components/README.md rename to packages/devextreme-schematics/src/migrate-config-components/README.md index 16b77205d..b6b9aee76 100644 --- a/packages/devextreme-schematics/src/migrate-nested-components/README.md +++ b/packages/devextreme-schematics/src/migrate-config-components/README.md @@ -1,10 +1,10 @@ -# migrate-nested-components +# migrate-config-components -A schematic that migrates deprecated nested DevExtreme components to the new structure. +A schematic that migrates deprecated nested DevExtreme components to the new config-based structure. ## Description -This schematic automatically migrates your Angular application to use the latest DevExtreme component structure by replacing deprecated nested components with their new equivalents. The migration command is available as a top-level DevExtreme CLI command and can be run from any directory. +This schematic automatically migrates your Angular application to use the latest DevExtreme component structure by replacing deprecated nested components (such as ``) with their new config-based equivalents (such as ``). The migration command is available as a top-level DevExtreme CLI command and can be run from any directory. ## Usage @@ -13,7 +13,7 @@ This schematic automatically migrates your Angular application to use the latest The migration command can be run from any directory and will work both inside and outside Angular workspaces: ```bash -devextreme migrate-nested-components +devextreme migrate angular-config-components ``` ### Options (All Optional) @@ -26,19 +26,19 @@ devextreme migrate-nested-components ```bash # Migrate all HTML templates and inline templates (using defaults) -devextreme migrate-nested-components +devextreme migrate angular-config-components # Migrate only specific files -devextreme migrate-nested-components --include="src/app/**/*.html,src/shared/**/*.html" +devextreme migrate angular-config-components --include="src/app/**/*.html,src/shared/**/*.html" # Preview changes without applying them -devextreme migrate-nested-components --dry +devextreme migrate angular-config-components --dry # Migrate only HTML templates, skip inline templates -devextreme migrate-nested-components --script-include="" +devextreme migrate angular-config-components --script-include="" # Combine multiple options -devextreme migrate-nested-components --include="*.html,*.ts" --dry +devextreme migrate angular-config-components --include="*.html,*.ts" --dry ``` ### Via Angular CLI @@ -46,14 +46,14 @@ devextreme migrate-nested-components --include="*.html,*.ts" --dry Alternatively, you can run the schematic directly with Angular CLI: ```bash -ng g devextreme-schematics:migrate-nested-components +ng g devextreme-schematics:migrate-config-components ``` ## What it does -This schematic automatically updates your templates to replace deprecated nested components with the new component structure. For example: +This schematic automatically updates your templates to replace deprecated nested components with the new config-based component structure. For example: -**Before:** +**Before (deprecated nested):** ```html @@ -61,7 +61,7 @@ This schematic automatically updates your templates to replace deprecated nested ``` -**After:** +**After (config-based):** ```html @@ -69,6 +69,10 @@ This schematic automatically updates your templates to replace deprecated nested ``` +### Why migrate? + +DevExtreme Angular components are moving from a nested structure (e.g., ``) to a config-based structure (e.g., ``) for improved clarity, maintainability, and future compatibility. This schematic automates the migration process to help you keep your codebase up to date with the latest DevExtreme standards. + ## Requirements - Node.js and npm/yarn installed diff --git a/packages/devextreme-schematics/src/migrate-config-components/index.ts b/packages/devextreme-schematics/src/migrate-config-components/index.ts new file mode 100644 index 000000000..efabc35e2 --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-config-components/index.ts @@ -0,0 +1,79 @@ +import { Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; +import { applyHostAwareTemplateMigrations, applyInlineComponentTemplateMigrations } from './template-migrator'; +import mapping from './mappings/deprecated-config-map.json'; + +export interface Options { + include?: string[]; + dry?: boolean; + scriptInclude?: string[]; +} + +export function migrateConfigComponents(options: Options = {}): Rule { + // Accept --include as array or comma-separated string + let include: string[] = ['**/*.html']; + const rawInclude = options.include; + if (Array.isArray(rawInclude) && rawInclude.length) { + include = rawInclude; + } else if (typeof rawInclude === 'string' && rawInclude) { + let str = (rawInclude as string).trim(); + if (str.startsWith('[') && str.endsWith(']')) { + str = str.slice(1, -1); + } + include = str.split(',').map((s: string) => s.trim()).filter((s: string) => !!s); + } + + // Coerce string 'true'/'false' to boolean for dry option + let dryFlag: boolean = false; + if (typeof options.dry === 'string') { + dryFlag = options.dry === 'true'; + } else { + dryFlag = !!options.dry; + } + + // Accept --script-include as array or comma-separated string + let scriptGlobs: string[] = ['**/*.ts', '**/*.js']; + const rawScriptInclude = options.scriptInclude; + if (Array.isArray(rawScriptInclude) && rawScriptInclude.length) { + scriptGlobs = rawScriptInclude; + } else if (typeof rawScriptInclude === 'string' && rawScriptInclude) { + let str = (rawScriptInclude as string).trim(); + if (str.startsWith('[') && str.endsWith(']')) { + str = str.slice(1, -1); + } + scriptGlobs = str.split(',').map((s: string) => s.trim()).filter((s: string) => !!s); + } + + return async (tree: Tree, ctx: SchematicContext): Promise => { + ctx.logger.info(`[config-migrator] Starting…`); + + type HostMap = Record>; + const hostMap = mapping as unknown as HostMap; + + const processedMapping = Object.entries(hostMap).map(([hostComponentName, map]) => { + const hostSelector = map._hostSelector ?? componentToSelectorGuess(hostComponentName); + const configMap: Record = Object.fromEntries( + Object.entries(map).filter(([k]) => k !== '_hostSelector') + ); + return { hostSelector, configMap }; + }) as import('./template-migrator').HostRule[]; + + // External HTML templates + await applyHostAwareTemplateMigrations(tree, { + includeGlobs: include, + rules: processedMapping + }, { dryRun: dryFlag, logger: ctx.logger }); + + // Inline templates inside component decorators (ts/js) + await applyInlineComponentTemplateMigrations(tree, { + includeGlobs: [], rules: processedMapping + }, { dryRun: dryFlag, logger: ctx.logger }, scriptGlobs); + + ctx.logger.info(`[config-migrator] Done.`); + }; +} + +// Fallback if _hostSelector is missing: "DxFooBarComponent" -> "dx-foo-bar" +function componentToSelectorGuess(componentName: string): string { + const core = componentName.replace(/Component$/, '').replace(/^Dx/, 'dx-'); + return core.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); +} diff --git a/packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json b/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json similarity index 99% rename from packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json rename to packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json index 362637e3f..f0694b740 100644 --- a/packages/devextreme-schematics/src/migrate-nested-components/mappings/deprecated-nested-map.json +++ b/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json @@ -1,7 +1,7 @@ { "_generated": true, - "_description": "Mapping: host component -> { _hostSelector?: string, generic nested selector : component-specific selector }", - "_note": "Do not edit manually; run scripts/generate-deprecated-nested-map.mjs", + "_description": "Mapping: host component -> { _hostSelector?: string, generic config selector : component-specific selector }", + "_note": "Do not edit manually; run scripts/generate-deprecated-config-map.mjs", "DxAccordionComponent": { "_hostSelector": "dx-accordion", "dxi-item": "dxi-accordion-item" diff --git a/packages/devextreme-schematics/src/migrate-config-components/schema.json b/packages/devextreme-schematics/src/migrate-config-components/schema.json new file mode 100644 index 000000000..60998c7f1 --- /dev/null +++ b/packages/devextreme-schematics/src/migrate-config-components/schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "SchematicsDevextremeMigrateConfigComponents", + "title": "Migrate deprecated config components to new ones", + "type": "object", + "properties": { + "include": { + "oneOf": [ + { "type": "array", "items": { "type": "string" } }, + { "type": "string" } + ], + "description": "Glob(s) of template files to include (default: **/*.html)" + }, + "scriptInclude": { + "oneOf": [ + { "type": "array", "items": { "type": "string" } }, + { "type": "string" } + ], + "description": "Glob(s) for TS/JS files to scan for inline @Component({ template }) (default: ['**/*.ts','**/*.js']; set to [] to disable)" + }, + "dry": { + "oneOf": [ + { "type": "boolean", "default": false }, + { "type": "string", "enum": ["true", "false"], "default": "false" } + ] + } + } +} \ No newline at end of file diff --git a/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts similarity index 86% rename from packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts rename to packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts index b47c635d6..058c8375e 100644 --- a/packages/devextreme-schematics/src/migrate-nested-components/template-migrator.ts +++ b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts @@ -1,18 +1,34 @@ import { Tree } from '@angular-devkit/schematics'; import * as parse5 from 'parse5'; +import * as path from 'path'; +import picomatch from 'picomatch'; // Dynamically require TypeScript if available; skip inline template migration if not. -let ts: any; -try { - // tslint:disable-next-line:no-var-requires - ts = require('typescript'); -} catch { - ts = null; // Will be checked before inline processing +// Dynamically require TypeScript if available; skip inline template migration if not. +let ts: any = null; +const tsResolutionErrors: string[] = []; +const tsResolutionPaths = [ + __dirname, + process.cwd(), + path.dirname(require.main?.filename || ''), +]; +for (const p of tsResolutionPaths) { + try { + // tslint:disable-next-line:no-var-requires + ts = require(require.resolve('typescript', { paths: [p] })); + break; + } catch (err) { + tsResolutionErrors.push(`Failed to resolve TypeScript from ${p}: ${err?.message || err}`); + } +} +if (!ts) { + try { + // tslint:disable-next-line:no-var-requires + ts = require('typescript'); + } catch (err) { + tsResolutionErrors.push(`Failed to require global TypeScript: ${err?.message || err}`); + } } - -// Ensure picomatch is loaded as a callable matcher factory in all module systems -// tslint:disable-next-line:no-var-requires -const picomatch: any = require('picomatch'); // Minimal parse5 types for our usage interface P5Node { [k: string]: any; } @@ -23,7 +39,7 @@ interface P5Document extends P5Node { export interface HostRule { hostSelector: string; - nestedMap: Record; + configMap: Record; } export interface RunnerOptions { @@ -82,10 +98,17 @@ export async function applyInlineComponentTemplateMigrations( return; } if (!ts) { - exec.logger.warn( - '[nested-migrator] Skipping inline template migration (TypeScript not available). ' + - 'Install "typescript" if you want inline template support.'); - return; + exec.logger.warn( + '[config-migrator] Skipping inline template migration (TypeScript not available).\n' + + 'Resolution attempts and errors:\n' + + tsResolutionErrors.map(e => ' - ' + e).join('\n') + '\n' + + 'How to fix:\n' + + ' 1. Install "typescript" in your project root: `npm install typescript --save-dev`\n' + + ' 2. If using the CLI globally, run: `npm install -g typescript`\n' + + ' 3. If you use npx, add typescript to your workspace or global node_modules.\n' + + ' 4. See README for troubleshooting global CLI usage and npm linking issues.' + ); + return; } const matcher = picomatch(scriptGlobs); tree.visit(filePath => { @@ -223,7 +246,7 @@ export function transformTemplate( if (!oldName) { return; } - const newName = rule.nestedMap[oldName]; + const newName = rule.configMap[oldName]; if (!newName) { return; } diff --git a/packages/devextreme-schematics/src/migrate-nested-components/index.ts b/packages/devextreme-schematics/src/migrate-nested-components/index.ts deleted file mode 100644 index e434d5cd7..000000000 --- a/packages/devextreme-schematics/src/migrate-nested-components/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; -import { applyHostAwareTemplateMigrations, applyInlineComponentTemplateMigrations } from './template-migrator'; -import mapping from './mappings/deprecated-nested-map.json'; - -export interface Options { - include?: string[]; - dry?: boolean; - scriptInclude?: string[]; -} - -export function migrateNestedComponents(options: Options = {}): Rule { - const include = options.include?.length ? options.include : ['**/*.html']; - - return async (tree: Tree, ctx: SchematicContext): Promise => { - ctx.logger.info(`[nested-migrator] Starting…`); - - type HostMap = Record>; - const hostMap = mapping as unknown as HostMap; - - const processedMapping = Object.entries(hostMap).map(([hostComponentName, map]) => { - const hostSelector = map._hostSelector ?? componentToSelectorGuess(hostComponentName); - const nestedMap: Record = Object.fromEntries( - Object.entries(map).filter(([k]) => k !== '_hostSelector') - ); - return { hostSelector, nestedMap }; - }); - - // External HTML templates - await applyHostAwareTemplateMigrations(tree, { - includeGlobs: include, - rules: processedMapping - }, { dryRun: !!options.dry, logger: ctx.logger }); - - // Inline templates inside component decorators (ts/js) - const scriptGlobs = options.scriptInclude === undefined ? ['**/*.ts', '**/*.js'] : options.scriptInclude; - await applyInlineComponentTemplateMigrations(tree, { - includeGlobs: [], rules: processedMapping - }, { dryRun: !!options.dry, logger: ctx.logger }, scriptGlobs); - - ctx.logger.info(`[nested-migrator] Done.`); - }; -} - -// Fallback if _hostSelector is missing: "DxFooBarComponent" -> "dx-foo-bar" -function componentToSelectorGuess(componentName: string): string { - const core = componentName.replace(/Component$/, '').replace(/^Dx/, 'dx-'); - return core.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); -} diff --git a/packages/devextreme-schematics/src/migrate-nested-components/schema.json b/packages/devextreme-schematics/src/migrate-nested-components/schema.json deleted file mode 100644 index 88ce925b3..000000000 --- a/packages/devextreme-schematics/src/migrate-nested-components/schema.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "$id": "SchematicsDevextremeMigrateNestedComponents", - "title": "Migrate deprecated nested components to new ones", - "type": "object", - "properties": { - "include": { - "type": "array", - "items": { "type": "string" }, - "description": "Glob(s) of template files to include (default: **/*.html)" - }, - "scriptInclude": { - "type": "array", - "items": { "type": "string" }, - "description": "Glob(s) for TS/JS files to scan for inline @Component({ template }) (default: ['**/*.ts','**/*.js']; set to [] to disable)" - }, - "dry": { "type": "boolean", "default": false } - } -} \ No newline at end of file From 47636323d65c09649678d4ca6a6e8f7b939a6c13 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 22 Oct 2025 10:48:28 +0400 Subject: [PATCH 4/8] Update packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/migrate-config-components/template-migrator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts index 058c8375e..0e048bd19 100644 --- a/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts +++ b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts @@ -3,7 +3,6 @@ import * as parse5 from 'parse5'; import * as path from 'path'; import picomatch from 'picomatch'; -// Dynamically require TypeScript if available; skip inline template migration if not. // Dynamically require TypeScript if available; skip inline template migration if not. let ts: any = null; const tsResolutionErrors: string[] = []; From bbb6d2b206904c11dad25d95872ee4848139faea Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 22 Oct 2025 10:51:06 +0400 Subject: [PATCH 5/8] Update packages/devextreme-schematics/src/collection.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/devextreme-schematics/src/collection.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme-schematics/src/collection.json b/packages/devextreme-schematics/src/collection.json index a705828e2..f25d8fc9e 100644 --- a/packages/devextreme-schematics/src/collection.json +++ b/packages/devextreme-schematics/src/collection.json @@ -26,7 +26,7 @@ "schema": "./add-view/schema.json" }, "migrate-config-components": { - "description": "Migrate DevExtreme configurationcomponents to the latest version.", + "description": "Migrate DevExtreme configuration components to the latest version.", "factory": "./migrate-config-components/index#migrateConfigComponents", "schema": "./migrate-config-components/schema.json" } From a5f566a9c9d84535aae0e9be705ace36d414ac95 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Wed, 22 Oct 2025 10:51:59 +0400 Subject: [PATCH 6/8] Update packages/devextreme-schematics/src/collection.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/devextreme-schematics/src/collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devextreme-schematics/src/collection.json b/packages/devextreme-schematics/src/collection.json index f25d8fc9e..47df733b2 100644 --- a/packages/devextreme-schematics/src/collection.json +++ b/packages/devextreme-schematics/src/collection.json @@ -27,8 +27,8 @@ }, "migrate-config-components": { "description": "Migrate DevExtreme configuration components to the latest version.", - "factory": "./migrate-config-components/index#migrateConfigComponents", - "schema": "./migrate-config-components/schema.json" + "factory": "./migrate-config-components/index#migrateConfigComponents", + "schema": "./migrate-config-components/schema.json" } } } From 7ea9453ee53c68f89fd587487392d79bca27b946 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Fri, 24 Oct 2025 17:27:16 +0400 Subject: [PATCH 7/8] type import fix --- .../src/migrate-config-components/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devextreme-schematics/src/migrate-config-components/index.ts b/packages/devextreme-schematics/src/migrate-config-components/index.ts index efabc35e2..a61430227 100644 --- a/packages/devextreme-schematics/src/migrate-config-components/index.ts +++ b/packages/devextreme-schematics/src/migrate-config-components/index.ts @@ -1,4 +1,5 @@ import { Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; +import type { HostRule } from './template-migrator'; import { applyHostAwareTemplateMigrations, applyInlineComponentTemplateMigrations } from './template-migrator'; import mapping from './mappings/deprecated-config-map.json'; @@ -55,7 +56,7 @@ export function migrateConfigComponents(options: Options = {}): Rule { Object.entries(map).filter(([k]) => k !== '_hostSelector') ); return { hostSelector, configMap }; - }) as import('./template-migrator').HostRule[]; + }) as HostRule[]; // External HTML templates await applyHostAwareTemplateMigrations(tree, { From fae4fb9289cb8387e5259c1477439bf4f4b9ac03 Mon Sep 17 00:00:00 2001 From: Arman Jivanyan Date: Fri, 24 Oct 2025 17:41:23 +0400 Subject: [PATCH 8/8] Text updates after TW Review --- .../src/applications/application.angular.js | 2 +- packages/devextreme-cli/src/commands.json | 8 ++++---- .../devextreme-schematics/src/collection.json | 2 +- .../mappings/deprecated-config-map.json | 2 +- .../src/migrate-config-components/schema.json | 4 ++-- .../template-migrator.ts | 15 +++++++-------- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/devextreme-cli/src/applications/application.angular.js b/packages/devextreme-cli/src/applications/application.angular.js index 321dbd22c..3e01e7751 100644 --- a/packages/devextreme-cli/src/applications/application.angular.js +++ b/packages/devextreme-cli/src/applications/application.angular.js @@ -190,7 +190,7 @@ const migrateConfigComponents = async(options = {}) => { await runCommand('npm', ['install', '-g', `${collectionName}@${schematicsVersion}`], { stdio: 'inherit' }); console.log('Installation completed successfully.'); } catch(error) { - console.error('Failed to install devextreme-schematics. Please install it manually:'); + console.error('Failed to install devextreme-schematics. Please install manually:'); console.error(`npm install -g ${collectionName}@${schematicsVersion}`); process.exit(1); } diff --git a/packages/devextreme-cli/src/commands.json b/packages/devextreme-cli/src/commands.json index 43e4321f8..58e6519fe 100644 --- a/packages/devextreme-cli/src/commands.json +++ b/packages/devextreme-cli/src/commands.json @@ -54,16 +54,16 @@ "usage": "devextreme migrate [options]", "arguments": [{ "name": "angular-config-components", - "description": "Migrate DevExtreme configuration components to the latest version.", + "description": "Migrate to the latest DevExtreme configuration components.", "options": [{ "name": "--include", - "description": "Glob patterns of template files to include (default: **/*.html). You can pass multiple patterns as a comma-separated string (e.g. \"**/a.html,**/b.html\") or as an array (e.g. [\"**/a.html\",\"**/b.html\"])." + "description": "Template file glob patterns to include (default: **/*.html). You can pass multiple patterns as a comma-separated string (e.g. \"**/a.html,**/b.html\") or as an array (e.g. [\"**/a.html\",\"**/b.html\"])." }, { "name": "--script-include", - "description": "Glob patterns for TypeScript/JavaScript files to scan for inline @Component({ template }) (default: **/*.ts,**/*.js). You can pass multiple patterns as a comma-separated string or as an array. Set to empty to disable." + "description": "TypeScript/JavaScript file glob patterns to scan for inline templates (default: **/*.ts,**/*.js). You can pass multiple patterns as a comma-separated string or as an array. Pass an empty value ('' or []) to disable." }, { "name": "--dry", - "description": "Run in dry mode to preview changes without applying them (default: false)" + "description": "Run in dry mode to preview changes without applying them (default: false)." }] }] }, { diff --git a/packages/devextreme-schematics/src/collection.json b/packages/devextreme-schematics/src/collection.json index 47df733b2..10b4a2206 100644 --- a/packages/devextreme-schematics/src/collection.json +++ b/packages/devextreme-schematics/src/collection.json @@ -26,7 +26,7 @@ "schema": "./add-view/schema.json" }, "migrate-config-components": { - "description": "Migrate DevExtreme configuration components to the latest version.", + "description": "Migrate to the latest DevExtreme configuration components.", "factory": "./migrate-config-components/index#migrateConfigComponents", "schema": "./migrate-config-components/schema.json" } diff --git a/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json b/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json index f0694b740..3532e4a5b 100644 --- a/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json +++ b/packages/devextreme-schematics/src/migrate-config-components/mappings/deprecated-config-map.json @@ -1,7 +1,7 @@ { "_generated": true, "_description": "Mapping: host component -> { _hostSelector?: string, generic config selector : component-specific selector }", - "_note": "Do not edit manually; run scripts/generate-deprecated-config-map.mjs", + "_note": "Do not edit manually. Run scripts/generate-deprecated-config-map.mjs to generate a new map.", "DxAccordionComponent": { "_hostSelector": "dx-accordion", "dxi-item": "dxi-accordion-item" diff --git a/packages/devextreme-schematics/src/migrate-config-components/schema.json b/packages/devextreme-schematics/src/migrate-config-components/schema.json index 60998c7f1..66012f3ff 100644 --- a/packages/devextreme-schematics/src/migrate-config-components/schema.json +++ b/packages/devextreme-schematics/src/migrate-config-components/schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/schema", "$id": "SchematicsDevextremeMigrateConfigComponents", - "title": "Migrate deprecated config components to new ones", + "title": "Migrate to the latest DevExtreme configuration components.", "type": "object", "properties": { "include": { @@ -16,7 +16,7 @@ { "type": "array", "items": { "type": "string" } }, { "type": "string" } ], - "description": "Glob(s) for TS/JS files to scan for inline @Component({ template }) (default: ['**/*.ts','**/*.js']; set to [] to disable)" + "description": "Glob(s) of TS/JS files to scan for inline templates (default: ['**/*.ts','**/*.js']; set empty ('' or []) to disable)" }, "dry": { "oneOf": [ diff --git a/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts index 0e048bd19..4ec4c0b53 100644 --- a/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts +++ b/packages/devextreme-schematics/src/migrate-config-components/template-migrator.ts @@ -17,7 +17,7 @@ for (const p of tsResolutionPaths) { ts = require(require.resolve('typescript', { paths: [p] })); break; } catch (err) { - tsResolutionErrors.push(`Failed to resolve TypeScript from ${p}: ${err?.message || err}`); + tsResolutionErrors.push(`Failed to import TypeScript from ${p}: ${err?.message || err}`); } } if (!ts) { @@ -25,7 +25,7 @@ if (!ts) { // tslint:disable-next-line:no-var-requires ts = require('typescript'); } catch (err) { - tsResolutionErrors.push(`Failed to require global TypeScript: ${err?.message || err}`); + tsResolutionErrors.push(`Failed to import TypeScript: ${err?.message || err}`); } } @@ -98,14 +98,13 @@ export async function applyInlineComponentTemplateMigrations( } if (!ts) { exec.logger.warn( - '[config-migrator] Skipping inline template migration (TypeScript not available).\n' + + '[config-migrator] Failed to import TypeScript. Skipping inline template migration.\n' + 'Resolution attempts and errors:\n' + tsResolutionErrors.map(e => ' - ' + e).join('\n') + '\n' + - 'How to fix:\n' + - ' 1. Install "typescript" in your project root: `npm install typescript --save-dev`\n' + - ' 2. If using the CLI globally, run: `npm install -g typescript`\n' + - ' 3. If you use npx, add typescript to your workspace or global node_modules.\n' + - ' 4. See README for troubleshooting global CLI usage and npm linking issues.' + 'To resolve this issue, perform one of the following steps:\n' + + ' 1. Install the "typescript" package in your project root: `npm install typescript --save-dev`\n' + + ' 2. Install the "typescript" package globally on your machine: `npm install -g typescript`\n' + + 'Refer to the README for further troubleshooting information.' ); return; }