Skip to content

Commit 78b8bce

Browse files
committed
Add angular nested components migration command
1 parent 160c8c6 commit 78b8bce

File tree

13 files changed

+1709
-10
lines changed

13 files changed

+1709
-10
lines changed

packages/devextreme-cli/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ if(args.help) {
3939
const run = async(commands, options) => {
4040
if(application.isApplicationCommand(commands[0])) {
4141
await application.run(commands, options, devextremeConfig.read());
42+
} else if(application.isMigrationCommand(commands[0])) {
43+
await application.run(commands, options, { applicationEngine: 'angular' });
4244
} else if(themeBuilder.isThemeBuilderCommand(commands[0])) {
4345
options.command = commands[0];
4446
themeBuilder.run(options);

packages/devextreme-cli/src/application.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const isApplicationCommand = (command) => {
99
return [ 'new', 'add' ].includes(command);
1010
};
1111

12+
const isMigrationCommand = (command) => {
13+
return command === 'migrate-nested-components';
14+
};
15+
1216
const handleWrongAppType = (appType, command) => {
1317
console.error(`The '${appType}' application type is not valid`);
1418
printHelp(command);
@@ -30,6 +34,11 @@ const createReact = async(appName, options, command) => {
3034
};
3135

3236
const run = async(commands, options, devextremeConfig) => {
37+
if(commands[0] === 'migrate-nested-components') {
38+
await angularApplication.migrateNestedComponents(options);
39+
return;
40+
}
41+
3342
if(!commands[1]) {
3443
console.error('Command is incomplete. Please specify parameters.');
3544
printHelp(commands[0]);
@@ -104,5 +113,6 @@ const run = async(commands, options, devextremeConfig) => {
104113

105114
module.exports = {
106115
isApplicationCommand,
116+
isMigrationCommand,
107117
run
108118
};

packages/devextreme-cli/src/applications/application.angular.js

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,24 @@ async function runNgCommand(commandArguments, commandOptions, commandConfig) {
5353
}
5454

5555
function localPackageExists(packageName) {
56+
// Check local node_modules first
5657
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
57-
if(!fs.existsSync(nodeModulesPath)) {
58-
return;
58+
if(fs.existsSync(nodeModulesPath)) {
59+
const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json');
60+
if(fs.existsSync(packageJsonPath)) {
61+
return true;
62+
}
5963
}
6064

61-
const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json');
62-
return fs.existsSync(packageJsonPath);
65+
// Check if globally installed by trying to resolve the package
66+
try {
67+
require.resolve(`${packageName}/package.json`);
68+
return true;
69+
} catch(e) {
70+
// Package not found globally
71+
}
72+
73+
return false;
6374
}
6475

6576
const hasSutableNgCli = async() => {
@@ -152,6 +163,70 @@ const addView = (viewName, options) => {
152163
runSchematicCommand('add-view', schematicOptions);
153164
};
154165

166+
const migrateNestedComponents = async(options = {}) => {
167+
const collectionName = 'devextreme-schematics';
168+
169+
// Check if devextreme-schematics is installed
170+
if(!localPackageExists(collectionName)) {
171+
const prompts = require('prompts');
172+
173+
console.log(`\nThe '${collectionName}' package is required to run this command.`);
174+
175+
const response = await prompts({
176+
type: 'confirm',
177+
name: 'install',
178+
message: `Would you like to install '${collectionName}' now?`,
179+
initial: true
180+
});
181+
182+
if(!response.install) {
183+
console.log('Migration cancelled. Please install devextreme-schematics manually:');
184+
console.log(`npm install -g ${collectionName}@${schematicsVersion}`);
185+
process.exit(1);
186+
}
187+
188+
console.log(`Installing ${collectionName}@${schematicsVersion}...`);
189+
try {
190+
await runCommand('npm', ['install', '-g', `${collectionName}@${schematicsVersion}`], { stdio: 'inherit' });
191+
console.log('Installation completed successfully.');
192+
} catch(error) {
193+
console.error('Failed to install devextreme-schematics. Please install it manually:');
194+
console.error(`npm install -g ${collectionName}@${schematicsVersion}`);
195+
process.exit(1);
196+
}
197+
}
198+
199+
const schematicOptions = {
200+
...options
201+
};
202+
203+
if(schematicOptions.include && typeof schematicOptions.include === 'string') {
204+
schematicOptions.include = schematicOptions.include.split(',').map(s => s.trim());
205+
}
206+
if(schematicOptions.scriptInclude && typeof schematicOptions.scriptInclude === 'string') {
207+
schematicOptions.scriptInclude = schematicOptions.scriptInclude.split(',').map(s => s.trim());
208+
}
209+
210+
const commandArguments = ['schematics', `${collectionName}:migrate-nested-components`];
211+
212+
const { [depsVersionTagOptionName]: _, ...optionsToArguments } = schematicOptions; // eslint-disable-line no-unused-vars
213+
for(let option in optionsToArguments) {
214+
const value = optionsToArguments[option];
215+
if(value !== undefined && value !== null && value !== '') {
216+
if(Array.isArray(value)) {
217+
if(value.length > 0) {
218+
commandArguments.push(`--${dasherize(option)}=${value.join(',')}`);
219+
}
220+
} else {
221+
commandArguments.push(`--${dasherize(option)}=${value}`);
222+
}
223+
}
224+
}
225+
226+
// Use runCommand directly with npx to work outside Angular workspace
227+
return runCommand('npx', commandArguments, { stdio: 'inherit' });
228+
};
229+
155230
const changeMainTs = (appPath) => {
156231
const filePath = path.join(appPath, 'src', 'main.ts');
157232

@@ -174,5 +249,6 @@ module.exports = {
174249
install,
175250
create,
176251
addTemplate,
177-
addView
252+
addView,
253+
migrateNestedComponents
178254
};

packages/devextreme-cli/src/commands.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@
4848
"name": "devextreme-angular",
4949
"description": "Add DevExtreme to an Angular application"
5050
}]
51+
}, {
52+
"name": "migrate-nested-components",
53+
"description": "Migrate deprecated nested DevExtreme components to new ones",
54+
"usage": "devextreme migrate-nested-components [options]",
55+
"options": [{
56+
"name": "--include",
57+
"description": "Glob patterns of template files to include (default: **/*.html). Separate multiple patterns with commas."
58+
}, {
59+
"name": "--script-include",
60+
"description": "Glob patterns for TypeScript/JavaScript files to scan for inline @Component({ template }) (default: **/*.ts,**/*.js). Set to empty to disable."
61+
}, {
62+
"name": "--dry",
63+
"description": "Run in dry mode to preview changes without applying them (default: false)"
64+
}]
5165
}, {
5266
"name": "build-theme",
5367
"description": "Build a custom color scheme",

packages/devextreme-schematics/package-lock.json

Lines changed: 43 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/devextreme-schematics/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
"dependencies": {
2525
"@angular-devkit/core": "^17.3.17",
2626
"@angular-devkit/schematics": "^17.3.17",
27-
"@schematics/angular": "^17.3.17"
27+
"@schematics/angular": "^17.3.17",
28+
"parse5": "^7.1.2",
29+
"picomatch": "^4.0.2"
2830
},
2931
"devDependencies": {
3032
"@types/jasmine": "~3.10.18",

packages/devextreme-schematics/src/collection.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
"description": "Add a new view to app-template.",
2525
"factory": "./add-view/index",
2626
"schema": "./add-view/schema.json"
27+
},
28+
"migrate-nested-components": {
29+
"description": "Migrate nested DevExtreme components to the latest version.",
30+
"factory": "./migrate-nested-components/index#migrateNestedComponents",
31+
"schema": "./migrate-nested-components/schema.json"
2732
}
2833
}
2934
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# migrate-nested-components
2+
3+
A schematic that migrates deprecated nested DevExtreme components to the new structure.
4+
5+
## Description
6+
7+
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.
8+
9+
## Usage
10+
11+
### Via DevExtreme CLI (Recommended)
12+
13+
The migration command can be run from any directory and will work both inside and outside Angular workspaces:
14+
15+
```bash
16+
devextreme migrate-nested-components
17+
```
18+
19+
### Options (All Optional)
20+
21+
- `--include`: Glob patterns of template files to include (default: `**/*.html`). Separate multiple patterns with commas.
22+
- `--script-include`: Glob patterns for TypeScript/JavaScript files to scan for inline `@Component({ template })` (default: `**/*.ts,**/*.js`). Set to empty to disable.
23+
- `--dry`: Run in dry mode to preview changes without applying them (default: `false`)
24+
25+
### Examples
26+
27+
```bash
28+
# Migrate all HTML templates and inline templates (using defaults)
29+
devextreme migrate-nested-components
30+
31+
# Migrate only specific files
32+
devextreme migrate-nested-components --include="src/app/**/*.html,src/shared/**/*.html"
33+
34+
# Preview changes without applying them
35+
devextreme migrate-nested-components --dry
36+
37+
# Migrate only HTML templates, skip inline templates
38+
devextreme migrate-nested-components --script-include=""
39+
40+
# Combine multiple options
41+
devextreme migrate-nested-components --include="*.html,*.ts" --dry
42+
```
43+
44+
### Via Angular CLI
45+
46+
Alternatively, you can run the schematic directly with Angular CLI:
47+
48+
```bash
49+
ng g devextreme-schematics:migrate-nested-components
50+
```
51+
52+
## What it does
53+
54+
This schematic automatically updates your templates to replace deprecated nested components with the new component structure. For example:
55+
56+
**Before:**
57+
```html
58+
<dx-data-grid>
59+
<dxi-column field="name"></dxi-column>
60+
<dxi-column field="age"></dxi-column>
61+
</dx-data-grid>
62+
```
63+
64+
**After:**
65+
```html
66+
<dx-data-grid>
67+
<dxi-data-grid-column field="name"></dxi-data-grid-column>
68+
<dxi-data-grid-column field="age"></dxi-data-grid-column>
69+
</dx-data-grid>
70+
```
71+
72+
## Requirements
73+
74+
- Node.js and npm/yarn installed
75+
- Angular application with DevExtreme components (when running inside a workspace)
76+
- DevExtreme Angular version that supports the new component structure
77+
78+
## Notes
79+
80+
- The command can be run from any directory, not just Angular workspaces
81+
- When run outside an Angular workspace, the underlying Angular CLI will show appropriate error messages
82+
- All options are optional and have sensible defaults
83+
- The command uses the latest version of `devextreme-schematics` automatically

0 commit comments

Comments
 (0)