Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/devextreme-cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ 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])) {
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);
Expand Down
23 changes: 23 additions & 0 deletions packages/devextreme-cli/src/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ const isApplicationCommand = (command) => {
return [ 'new', 'add' ].includes(command);
};

const isMigrationCommand = (command) => {
return [ 'migrate' ].includes(command);
};

const handleWrongAppType = (appType, command) => {
console.error(`The '${appType}' application type is not valid`);
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']);

Expand All @@ -30,12 +39,25 @@ const createReact = async(appName, options, command) => {
};

const run = async(commands, options, devextremeConfig) => {

if(!commands[1]) {
console.error('Command is incomplete. Please specify parameters.');
printHelp(commands[0]);
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';
Expand Down Expand Up @@ -104,5 +126,6 @@ const run = async(commands, options, devextremeConfig) => {

module.exports = {
isApplicationCommand,
isMigrationCommand,
run
};
86 changes: 81 additions & 5 deletions packages/devextreme-cli/src/applications/application.angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() => {
Expand Down Expand Up @@ -152,6 +163,70 @@ const addView = (viewName, options) => {
runSchematicCommand('add-view', schematicOptions);
};

const migrateConfigComponents = 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 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-config-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');

Expand All @@ -174,5 +249,6 @@ module.exports = {
install,
create,
addTemplate,
addView
addView,
migrateConfigComponents
};
18 changes: 18 additions & 0 deletions packages/devextreme-cli/src/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@
"name": "devextreme-angular",
"description": "Add DevExtreme to an Angular application"
}]
}, {
"name": "migrate",
"description": "Migration commands for DevExtreme applications",
"usage": "devextreme migrate <change name> [options]",
"arguments": [{
"name": "angular-config-components",
"description": "Migrate to the latest DevExtreme configuration components.",
"options": [{
"name": "--include",
"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": "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)."
}]
}]
}, {
"name": "build-theme",
"description": "Build a custom color scheme",
Expand Down
27 changes: 27 additions & 0 deletions packages/devextreme-schematics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
55 changes: 51 additions & 4 deletions packages/devextreme-schematics/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/devextreme-schematics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@
"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",
"@types/node": "ts5.2",
"@types/picomatch": "^4.0.2",
"@types/semver": "^7.7.1",
"jasmine": "^2.99.0",
"rxjs": "^6.6.7",
Expand Down
5 changes: 5 additions & 0 deletions packages/devextreme-schematics/src/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"description": "Add a new view to app-template.",
"factory": "./add-view/index",
"schema": "./add-view/schema.json"
},
"migrate-config-components": {
"description": "Migrate to the latest DevExtreme configuration components.",
"factory": "./migrate-config-components/index#migrateConfigComponents",
"schema": "./migrate-config-components/schema.json"
}
}
}
Loading
Loading