Skip to content

Commit 8faba59

Browse files
Imlement cli commands for vue (#67)
* Implement vue * Update readme
1 parent a49e6a3 commit 8faba59

37 files changed

+1976
-15
lines changed

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ DevExtreme CLI is a set of command-line tools to be used with DevExtreme control
66
* [Add DevExtreme to an Existing Application](#add-devextreme)
77
* [Angular](#add-devextreme-angular)
88
* [React](#add-devextreme-react)
9+
* [Vue](#add-devextreme-vue)
910
* [DevExtreme Application](#devextreme-app)
1011
* [Angular](#devextreme-app-angular)
1112
* [React](#devextreme-app-react)
13+
* [Vue](#devextreme-app-vue)
1214
* [Command Line Arguments](#devextreme-app-arguments)
1315
* [Theme Builder](#theme-builder)
1416
* [One-off Usage](#theme-builder-one-off)
@@ -60,6 +62,22 @@ Add DevExtreme to a React application:
6062
> devextreme add devextreme-react
6163
```
6264

65+
### <a name="add-devextreme-react"></a>Vue
66+
67+
Add DevExtreme to a Vue application:
68+
69+
#### <a name="add-devextreme-vue-no-global"></a>Without global package usage
70+
71+
```bash
72+
> npx -p devextreme-cli devextreme add devextreme-vue
73+
```
74+
75+
#### <a name="add-devextreme-vue-global"></a>Global package usage
76+
77+
```bash
78+
> devextreme add devextreme-vue
79+
```
80+
6381
## <a name="devextreme-app"></a>DevExtreme Application
6482

6583
### <a name="devextreme-app-angular"></a>Angular
@@ -124,6 +142,32 @@ Add a view to the DevExtreme application:
124142
> devextreme add view view-name [--icon]
125143
```
126144

145+
### <a name="devextreme-app-vue"></a>Vue
146+
147+
#### <a name="devextreme-app-vue-no-global"></a>Without global package usage
148+
149+
Create a new DevExtreme application:
150+
```bash
151+
> npx -p devextreme-cli devextreme new vue-app app-name [--layout][--empty]
152+
```
153+
154+
Add a view to the DevExtreme application:
155+
```bash
156+
> npx -p devextreme-cli devextreme add view view-name [--icon]
157+
```
158+
159+
#### <a name="devextreme-app-vue-global"></a>Global package usage
160+
161+
Create a new DevExtreme application:
162+
```bash
163+
> devextreme new vue-app app-name [--layout][--empty]
164+
```
165+
166+
Add a view to the DevExtreme application:
167+
```bash
168+
> devextreme add view view-name [--icon]
169+
```
170+
127171
### <a name="devextreme-app-arguments"></a>Command line arguments
128172

129173
* `--layout` – Specifies the type of a DevExtreme layout to add (default: `side-nav-outer-toolbar`)

commands/application.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const angularApplication = require('./application.angular');
22
const reactApplication = require('./application.react');
3+
const vueApplication = require('./application.vue');
34
const printHelp = require('./help').printHelp;
45

56
const isApplicationCommand = (command) => {
@@ -24,6 +25,11 @@ const run = (commands, options, devextremeConfig) => {
2425
return;
2526
}
2627

28+
if(commands[1] === 'vue-app') {
29+
vueApplication.create(commands[2] || 'my-app', options);
30+
return;
31+
}
32+
2733
console.error(`The '${commands[1]}' application type is not valid`);
2834
printHelp(commands[0]);
2935
} else {
@@ -38,6 +44,11 @@ const run = (commands, options, devextremeConfig) => {
3844
return;
3945
}
4046

47+
if(commands[1] === 'devextreme-vue') {
48+
vueApplication.install(options);
49+
return;
50+
}
51+
4152
if(commands[1] === 'angular-template') {
4253
angularApplication.addTemplate(commands[2], options);
4354
return;
@@ -57,6 +68,13 @@ const run = (commands, options, devextremeConfig) => {
5768
console.error('Invalid command');
5869
printHelp(commands[0]);
5970
}
71+
} else if(devextremeConfig.applicationEngine === 'vue') {
72+
if(commands[1] === 'view') {
73+
vueApplication.addView(commands[2], options);
74+
} else {
75+
console.error('Invalid command');
76+
printHelp(commands[0]);
77+
}
6078
} else {
6179
console.log('The DevExtreme application cannot be found');
6280
}

commands/application.react.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ const create = (appName, options) => {
8080
const humanizedName = stringUtils.humanize(appName);
8181
const templateOptions = Object.assign({}, options, {
8282
project: humanizedName,
83-
skipFolder: options.empty ? 'pages' : '',
8483
layout: promptsResult.layout
8584
});
8685
modifyIndexHtml(appPath, humanizedName);
@@ -108,6 +107,9 @@ const addTemplate = (appPath, appName, templateOptions) => {
108107
'./themes/generated/theme.base.css'];
109108

110109
templateCreator.moveTemplateFilesToProject(templateSourcePath, appPath, templateOptions);
110+
if(!templateOptions.empty) {
111+
addSamplePages(appPath);
112+
}
111113
preparePackageJsonForTemplate(packagePath, appName);
112114
updateJsonPropName(manifestPath, appName);
113115
install({}, appPath, styles);
@@ -163,6 +165,13 @@ const createPagesIndex = () => {
163165
fs.writeFileSync(pathToPagesIndex, '');
164166
};
165167

168+
const addSamplePages = (appPath) => {
169+
const templateSourcePath = path.join(__dirname, '..', 'templates', 'react', 'sample-pages');
170+
const pagesPath = path.join(appPath, 'src', 'pages');
171+
fs.mkdirSync(pagesPath);
172+
templateCreator.moveTemplateFilesToProject(templateSourcePath, pagesPath, {});
173+
};
174+
166175
const addView = (pageName, options) => {
167176
const componentName = getComponentPageName(pageName);
168177
const pathToPage = createPathToPage(pageName);

commands/application.vue.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
const runCommand = require('../utility/run-command');
2+
const path = require('path');
3+
const fs = require('fs');
4+
const createVueApp = require('@vue/cli/lib/create');
5+
const runPrompts = require('../utility/prompts');
6+
const templateCreator = require('../utility/template-creator');
7+
const packageJsonUtils = require('../utility/package-json-utils');
8+
const modifyJson = require('../utility/modify-json-file');
9+
const insertItemToArray = require('../utility/file-content').insertItemToArray;
10+
const moduleUtils = require('../utility/module');
11+
const stringUtils = require('../utility/string');
12+
const defaultStyles = [
13+
'devextreme/dist/css/dx.light.css',
14+
'devextreme/dist/css/dx.common.css'
15+
];
16+
const layouts = [
17+
{ value: 'side-nav-outer-toolbar', title: 'Side navigation (outer toolbar)' },
18+
{ value: 'side-nav-inner-toolbar', title: 'Side navigation (inner toolbar)' }
19+
];
20+
21+
const addDevextremeToPackageJson = (appPath, dxversion) => {
22+
const packagePath = path.join(appPath, 'package.json');
23+
const depends = [
24+
{ name: 'devextreme', version: dxversion },
25+
{ name: 'devextreme-vue', version: dxversion }
26+
];
27+
28+
packageJsonUtils.addDependencies(packagePath, depends);
29+
};
30+
31+
const preparePackageJsonForTemplate = (packagePath, appName) => {
32+
const depends = [
33+
{ name: 'node-sass', version: '^4.11.0' },
34+
{ name: 'vue-router', version: '^3.0.1' }
35+
];
36+
const devDepends = [
37+
{ name: 'devextreme-cli', version: 'next' },
38+
{ name: 'sass-loader', version: '^7.1.0' }
39+
];
40+
const scripts = [
41+
{ name: 'build-themes', value: 'devextreme build' },
42+
{ name: 'postinstall', value: 'npm run build-themes' }
43+
];
44+
45+
packageJsonUtils.addDependencies(packagePath, depends);
46+
packageJsonUtils.addDependencies(packagePath, devDepends, 'dev');
47+
packageJsonUtils.updateScripts(packagePath, scripts);
48+
updateJsonPropName(packagePath, appName);
49+
};
50+
51+
const updateJsonPropName = (path, name) => {
52+
modifyJson(path, content => {
53+
content.name = name;
54+
55+
return content;
56+
});
57+
};
58+
59+
const getLayout = (options) => {
60+
const currentLayout = layouts.filter((layout) => {
61+
return layout.value === options.layout;
62+
});
63+
64+
return currentLayout.length ? [currentLayout[0].value] : undefined;
65+
};
66+
67+
const create = (appName, options) => {
68+
const prompts = [
69+
{
70+
type: 'select',
71+
name: 'layout',
72+
message: 'What layout do you want to add?',
73+
choices: layouts
74+
}
75+
];
76+
77+
runPrompts(options, prompts, getLayout).then((promptsResult) => {
78+
createVueApp(appName, { default: true }).then(() => {
79+
const appPath = path.join(process.cwd(), appName);
80+
const humanizedName = stringUtils.humanize(appName);
81+
const templateOptions = Object.assign({}, options, {
82+
project: humanizedName,
83+
layout: promptsResult.layout
84+
});
85+
modifyIndexHtml(appPath, humanizedName);
86+
addTemplate(appPath, appName, templateOptions);
87+
});
88+
});
89+
};
90+
91+
const modifyIndexHtml = (appPath, appName) => {
92+
const indexHtmlPath = path.join(appPath, 'public', 'index.html');
93+
let htmlContent = fs.readFileSync(indexHtmlPath).toString();
94+
95+
htmlContent = htmlContent.replace(/<title>(\w+\s*)+<\/title>/, `<title>${appName}<\/title>`);
96+
htmlContent = htmlContent.replace('<body>', '<body class="dx-viewport">');
97+
fs.writeFileSync(indexHtmlPath, htmlContent);
98+
};
99+
100+
const addTemplate = (appPath, appName, templateOptions) => {
101+
const templateSourcePath = path.join(__dirname, '..', 'templates', 'vue', 'application');
102+
const packagePath = path.join(appPath, 'package.json');
103+
const styles = [
104+
'devextreme/dist/css/dx.common.css',
105+
'./themes/generated/theme.additional.css',
106+
'./themes/generated/theme.base.css'];
107+
108+
templateCreator.moveTemplateFilesToProject(templateSourcePath, appPath, templateOptions);
109+
if(!templateOptions.empty) {
110+
addSamplePages(appPath);
111+
}
112+
preparePackageJsonForTemplate(packagePath, appName);
113+
install({}, appPath, styles);
114+
};
115+
116+
const install = (options, appPath, styles) => {
117+
appPath = appPath ? appPath : process.cwd();
118+
const mainModulePath = path.join(appPath, 'src', 'main.js');
119+
addStylesToApp(mainModulePath, styles || defaultStyles);
120+
addDevextremeToPackageJson(appPath, options.dxversion || 'latest');
121+
122+
runCommand('npm', ['install'], { cwd: appPath });
123+
};
124+
125+
const addStylesToApp = (filePath, styles) => {
126+
styles.forEach(style => {
127+
moduleUtils.insertImport(filePath, style);
128+
});
129+
};
130+
131+
const addSamplePages = (appPath) => {
132+
const templateSourcePath = path.join(__dirname, '..', 'templates', 'vue', 'sample-pages');
133+
const pagesPath = createPathToPage(appPath);
134+
templateCreator.moveTemplateFilesToProject(templateSourcePath, pagesPath, {});
135+
};
136+
137+
const getComponentPageName = (viewName) => {
138+
return `${stringUtils.classify(viewName)}`;
139+
};
140+
141+
const getVueRoute = (viewName, componentName, pagePath) => {
142+
const components = `{\n layout: defaultLayout,\n content: ${componentName}\n }\n `;
143+
return `{\n path: "/${pagePath}",\n name: "${stringUtils.dasherize(viewName)}",\n meta: { requiresAuth: true },\n components: ${components}}`;
144+
};
145+
146+
const getNavigationData = (viewName, componentName, icon) => {
147+
const pagePath = stringUtils.dasherize(viewName);
148+
return {
149+
route: getVueRoute(viewName, componentName, pagePath),
150+
navigation: `{\n text: \'${stringUtils.humanize(viewName)}\',\n path: \'/${pagePath}\',\n icon: \'${icon}\'\n }`
151+
};
152+
};
153+
154+
const createPathToPage = (appPath) => {
155+
const pagesPath = path.join(appPath, 'src', 'views');
156+
157+
if(!fs.existsSync(pagesPath)) {
158+
fs.mkdirSync(pagesPath);
159+
}
160+
161+
return pagesPath;
162+
};
163+
164+
const addView = (pageName, options) => {
165+
const componentName = getComponentPageName(pageName);
166+
const pathToPage = createPathToPage(process.cwd());
167+
const pageTemplatePath = path.join(__dirname, '..', 'templates', 'vue', 'page');
168+
const routingModulePath = path.join(process.cwd(), 'src', 'router.js');
169+
const navigationModulePath = path.join(process.cwd(), 'src', 'app-navigation.js');
170+
const navigationData = getNavigationData(pageName, componentName, options && options.icon || 'home');
171+
172+
templateCreator.addPageToApp(pageName, pathToPage, pageTemplatePath);
173+
moduleUtils.insertImport(routingModulePath, `./views/${pageName}`, componentName, true);
174+
insertItemToArray(routingModulePath, navigationData.route);
175+
insertItemToArray(navigationModulePath, navigationData.navigation);
176+
};
177+
178+
module.exports = {
179+
install,
180+
create,
181+
addTemplate,
182+
addView
183+
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "devextreme-cli",
3-
"version": "1.1.0-beta.1",
3+
"version": "1.1.0-beta.2",
44
"description": "DevExtreme CLI",
55
"keywords": [
66
"devexpress",
@@ -25,6 +25,7 @@
2525
"minimist": "^1.2.0",
2626
"mustache": "^3.0.1",
2727
"node-sass": "^4.9.3",
28+
"@vue/cli": "^3.5.5",
2829
"prompts": "^2.0.4",
2930
"semver": "^5.6.0"
3031
},

templates/react/application/src/pages/display-data/display-data.js renamed to templates/react/sample-pages/display-data/display-data.js

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)