devextreme-cli 1.10.1 → 1.11.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/package.json +7 -5
  2. package/src/application.js +40 -20
  3. package/src/applications/application.angular.js +42 -9
  4. package/src/applications/application.nextjs.js +231 -0
  5. package/src/applications/application.react.js +41 -25
  6. package/src/applications/application.vue.js +23 -6
  7. package/src/templates/nextjs/application/.env +1 -0
  8. package/src/templates/nextjs/application/devextreme.json +63 -0
  9. package/src/templates/nextjs/application/next.config.mjs +32 -0
  10. package/src/templates/nextjs/application/src/app/actions/auth.ts +76 -0
  11. package/src/templates/nextjs/application/src/app/auth/[type]/page.tsx +49 -0
  12. package/src/templates/nextjs/application/src/app/layout.tsx +17 -0
  13. package/src/templates/nextjs/application/src/app/lib/session.ts +47 -0
  14. package/src/templates/nextjs/application/src/app/pages/layout.tsx +18 -0
  15. package/src/templates/nextjs/application/src/app-info.tsx +5 -0
  16. package/src/templates/nextjs/application/src/app-navigation.tsx +21 -0
  17. package/src/templates/nextjs/application/src/components/change-password-form/ChangePasswordForm.tsx +86 -0
  18. package/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.scss +19 -0
  19. package/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.tsx +107 -0
  20. package/src/templates/nextjs/application/src/components/footer/Footer.scss +12 -0
  21. package/src/templates/nextjs/application/src/components/footer/Footer.tsx +5 -0
  22. package/src/templates/nextjs/application/src/components/header/Header.scss +40 -0
  23. package/src/templates/nextjs/application/src/components/header/Header.tsx +38 -0
  24. package/src/templates/nextjs/application/src/components/index.tsx +7 -0
  25. package/src/templates/nextjs/application/src/components/login-form/LoginForm.scss +12 -0
  26. package/src/templates/nextjs/application/src/components/login-form/LoginForm.tsx +101 -0
  27. package/src/templates/nextjs/application/src/components/reset-password-form/ResetPasswordForm.scss +12 -0
  28. package/src/templates/nextjs/application/src/components/reset-password-form/ResetPasswordForm.tsx +78 -0
  29. package/src/templates/nextjs/application/src/components/side-navigation-menu/SideNavigationMenu.scss +71 -0
  30. package/src/templates/nextjs/application/src/components/side-navigation-menu/SideNavigationMenu.tsx +88 -0
  31. package/src/templates/nextjs/application/src/components/theme-switcher/ThemeSwitcher.tsx +21 -0
  32. package/src/templates/nextjs/application/src/components/user-panel/UserPanel.scss +51 -0
  33. package/src/templates/nextjs/application/src/components/user-panel/UserPanel.tsx +55 -0
  34. package/src/templates/nextjs/application/src/dx-styles.scss +106 -0
  35. package/src/templates/nextjs/application/src/layouts/index.tsx +3 -0
  36. package/src/templates/nextjs/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.scss +17 -0
  37. package/src/templates/nextjs/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.tsx +133 -0
  38. package/src/templates/nextjs/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.scss +10 -0
  39. package/src/templates/nextjs/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.tsx +119 -0
  40. package/src/templates/nextjs/application/src/layouts/single-card/single-card.scss +42 -0
  41. package/src/templates/nextjs/application/src/layouts/single-card/single-card.tsx +16 -0
  42. package/src/templates/nextjs/application/src/middleware.ts +46 -0
  43. package/src/templates/nextjs/application/src/theme.tsx +66 -0
  44. package/src/templates/nextjs/application/src/themes/metadata.additional.dark.json +11 -0
  45. package/src/templates/nextjs/application/src/themes/metadata.additional.json +11 -0
  46. package/src/templates/nextjs/application/src/themes/metadata.base.dark.json +8 -0
  47. package/src/templates/nextjs/application/src/themes/metadata.base.json +7 -0
  48. package/src/templates/nextjs/application/src/types.tsx +60 -0
  49. package/src/templates/nextjs/application/src/utils/default-user.tsx +7 -0
  50. package/src/templates/nextjs/application/src/utils/media-query.tsx +56 -0
  51. package/src/templates/nextjs/application/src/variables.scss +53 -0
  52. package/src/templates/nextjs/page/page.scss +0 -0
  53. package/src/templates/nextjs/page/page.tsx +13 -0
  54. package/src/templates/nextjs/sample-pages/home/home.scss +37 -0
  55. package/src/templates/nextjs/sample-pages/home/page.tsx +101 -0
  56. package/src/templates/nextjs/sample-pages/profile/page.tsx +61 -0
  57. package/src/templates/nextjs/sample-pages/profile/profile.scss +19 -0
  58. package/src/templates/nextjs/sample-pages/tasks/page.tsx +112 -0
  59. package/src/templates/nextjs/sample-pages/tasks/tasks.scss +3 -0
  60. package/src/templates/react/application/src/App.tsx +0 -1
  61. package/src/templates/react/application/src/components/change-password-form/ChangePasswordForm.tsx +2 -2
  62. package/src/templates/react/application/src/components/create-account-form/CreateAccountForm.scss +0 -2
  63. package/src/templates/react/application/src/components/create-account-form/CreateAccountForm.tsx +2 -2
  64. package/src/templates/react/application/src/components/footer/Footer.tsx +0 -1
  65. package/src/templates/react/application/src/components/header/Header.scss +1 -1
  66. package/src/templates/react/application/src/components/header/Header.tsx +0 -1
  67. package/src/templates/react/application/src/components/login-form/LoginForm.scss +0 -2
  68. package/src/templates/react/application/src/components/login-form/LoginForm.tsx +2 -2
  69. package/src/templates/react/application/src/components/reset-password-form/ResetPasswordForm.scss +0 -2
  70. package/src/templates/react/application/src/components/reset-password-form/ResetPasswordForm.tsx +2 -2
  71. package/src/templates/react/application/src/components/side-navigation-menu/SideNavigationMenu.scss +3 -3
  72. package/src/templates/react/application/src/components/side-navigation-menu/SideNavigationMenu.tsx +1 -1
  73. package/src/templates/react/application/src/components/theme-switcher/ThemeSwitcher.tsx +1 -1
  74. package/src/templates/react/application/src/components/user-panel/UserPanel.tsx +1 -1
  75. package/src/templates/react/application/src/dx-styles.scss +5 -3
  76. package/src/templates/react/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.tsx +11 -5
  77. package/src/templates/react/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.tsx +11 -5
  78. package/src/templates/react/application/src/layouts/single-card/single-card.scss +0 -2
  79. package/src/templates/react/application/src/layouts/single-card/single-card.tsx +0 -1
  80. package/src/templates/react/application/src/types.tsx +1 -2
  81. package/src/templates/react/application/src/utils/media-query.tsx +3 -1
  82. package/src/templates/react/application/src/utils/patches.scss +4 -4
  83. package/src/templates/react/sample-pages/home/home.scss +0 -2
  84. package/src/templates/react/sample-pages/home/home.tsx +1 -1
  85. package/src/templates/react/sample-pages/tasks/tasks.tsx +4 -4
  86. package/src/templates/vue-v3/application/src/components/header-toolbar.vue +1 -1
  87. package/src/templates/vue-v3/application/src/dx-styles.scss +4 -3
  88. package/src/templates/vue-v3/sample-pages/tasks-page.vue +1 -1
  89. package/src/utility/extract-deps-version-tag.js +13 -0
  90. package/src/utility/latest-versions.js +6 -3
  91. package/src/utility/module.js +11 -3
  92. package/src/utility/prompts/react-app-type.js +17 -0
  93. package/src/utility/prompts/transpiler.js +17 -0
  94. package/src/utility/run-command.js +10 -2
  95. package/src/utility/template-creator.js +8 -4
  96. package/src/utility/typescript-extension.js +1 -1
  97. package/src/templates/cra-template/LICENSE +0 -21
  98. package/src/templates/cra-template/README.md +0 -10
  99. package/src/templates/cra-template/package.json +0 -26
  100. package/src/templates/cra-template/template/README.md +0 -70
  101. package/src/templates/cra-template/template/gitignore +0 -23
  102. package/src/templates/cra-template/template/public/favicon.ico +0 -0
  103. package/src/templates/cra-template/template/public/index.html +0 -43
  104. package/src/templates/cra-template/template/src/App.css +0 -38
  105. package/src/templates/cra-template/template/src/App.js +0 -25
  106. package/src/templates/cra-template/template/src/App.test.js +0 -9
  107. package/src/templates/cra-template/template/src/index.js +0 -17
  108. package/src/templates/cra-template/template/src/logo.svg +0 -1
  109. package/src/templates/cra-template/template/src/reportWebVitals.js +0 -13
  110. package/src/templates/cra-template/template/src/setupTests.js +0 -5
  111. package/src/templates/cra-template/template.json +0 -14
  112. package/src/templates/cra-template-typescript/LICENSE +0 -21
  113. package/src/templates/cra-template-typescript/README.md +0 -20
  114. package/src/templates/cra-template-typescript/package.json +0 -27
  115. package/src/templates/cra-template-typescript/template/README.md +0 -46
  116. package/src/templates/cra-template-typescript/template/gitignore +0 -23
  117. package/src/templates/cra-template-typescript/template/public/index.html +0 -43
  118. package/src/templates/cra-template-typescript/template/src/App.css +0 -38
  119. package/src/templates/cra-template-typescript/template/src/App.test.tsx +0 -10
  120. package/src/templates/cra-template-typescript/template/src/App.tsx +0 -26
  121. package/src/templates/cra-template-typescript/template/src/index.tsx +0 -19
  122. package/src/templates/cra-template-typescript/template/src/logo.svg +0 -1
  123. package/src/templates/cra-template-typescript/template/src/reportWebVitals.ts +0 -13
  124. package/src/templates/cra-template-typescript/template/src/setupTests.ts +0 -5
  125. package/src/templates/cra-template-typescript/template.json +0 -19
  126. package/src/templates/react/application/src/App.test.tsx +0 -13
  127. package/src/templates/react/application/src/matchMediaMock.tsx +0 -14
  128. package/src/templates/react/application/src/polyfills.tsx +0 -2
  129. package/src/utility/extract-tooling-version.js +0 -13
  130. /package/src/templates/{cra-template-typescript/template → nextjs/application}/public/logo192.png +0 -0
  131. /package/src/templates/{cra-template-typescript/template → nextjs/application}/public/logo512.png +0 -0
  132. /package/src/templates/{cra-template-typescript/template → nextjs/application}/public/manifest.json +0 -0
  133. /package/src/templates/{cra-template-typescript/template → nextjs/application}/public/robots.txt +0 -0
  134. /package/src/templates/{cra-template-typescript/template → nextjs/application}/src/index.css +0 -0
  135. /package/src/templates/{cra-template-typescript/template → react/application}/public/favicon.ico +0 -0
  136. /package/src/templates/{cra-template/template → react/application}/public/logo192.png +0 -0
  137. /package/src/templates/{cra-template/template → react/application}/public/logo512.png +0 -0
  138. /package/src/templates/{cra-template/template → react/application}/public/manifest.json +0 -0
  139. /package/src/templates/{cra-template/template → react/application}/public/robots.txt +0 -0
  140. /package/src/templates/{cra-template/template → react/application}/src/index.css +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devextreme-cli",
3
- "version": "1.10.1",
3
+ "version": "1.11.0-beta.0",
4
4
  "description": "DevExtreme CLI",
5
5
  "keywords": [
6
6
  "devexpress",
@@ -43,7 +43,7 @@
43
43
  "minimist": "^1.2.8",
44
44
  "mustache": "^3.2.1",
45
45
  "prompts": "^2.4.2",
46
- "sass": "^1.86.3",
46
+ "sass": "^1.85.1",
47
47
  "semver": "^5.7.2",
48
48
  "strip-bom": "^4.0.0"
49
49
  },
@@ -51,6 +51,7 @@
51
51
  "@typescript-eslint/eslint-plugin": "^4.33.0",
52
52
  "@typescript-eslint/parser": "^4.33.0",
53
53
  "babel-eslint": "^10.1.0",
54
+ "create-vite": "6.3.1",
54
55
  "cross-env": "^5.2.1",
55
56
  "eslint": "^7.32.0",
56
57
  "eslint-config-angular": "^0.5.0",
@@ -58,7 +59,7 @@
58
59
  "eslint-plugin-angular": "^4.1.0",
59
60
  "eslint-plugin-jest": "^22.21.0",
60
61
  "eslint-plugin-prettier": "^4.2.1",
61
- "eslint-plugin-react": "^7.37.5",
62
+ "eslint-plugin-react": "^7.37.4",
62
63
  "eslint-plugin-react-hooks": "^4.6.2",
63
64
  "eslint-plugin-unused-imports": "^1.1.5",
64
65
  "eslint-plugin-vue": "^7.20.0",
@@ -70,7 +71,8 @@
70
71
  "tree-kill": "^1.2.2",
71
72
  "tree-kill-promise": "^1.0.12",
72
73
  "typescript": "^4.0.2",
73
- "typescript-eslint-parser": "^22.0.0"
74
+ "typescript-eslint-parser": "^22.0.0",
75
+ "wait-on": "8.0.0"
74
76
  },
75
- "gitHead": "c8a1d6b0ae5e608f67ed1fbbd1fba3427f3cbfcf"
77
+ "gitHead": "12a211ead8510e78eb1f33b6a34664823d73a4a7"
76
78
  }
@@ -1,12 +1,34 @@
1
1
  const angularApplication = require('./applications/application.angular');
2
2
  const reactApplication = require('./applications/application.react');
3
+ const nextjsApplication = require('./applications/application.nextjs');
3
4
  const vueApplication = require('./applications/application.vue');
5
+ const getReactAppType = require('./utility/prompts/react-app-type');
4
6
  const printHelp = require('./help').printHelp;
5
7
 
6
8
  const isApplicationCommand = (command) => {
7
9
  return [ 'new', 'add' ].includes(command);
8
10
  };
9
11
 
12
+ const handleWrongAppType = (appType, command) => {
13
+ console.error(`The '${appType}' application type is not valid`);
14
+ printHelp(command);
15
+ };
16
+
17
+ const createReact = async(appName, options, command) => {
18
+ const reactAppType = await getReactAppType(options['app-type']);
19
+
20
+ switch(reactAppType) {
21
+ case 'vite':
22
+ await reactApplication.create(appName, options);
23
+ return;
24
+ case 'nextjs':
25
+ await nextjsApplication.create(appName, options);
26
+ return;
27
+ default:
28
+ handleWrongAppType(reactAppType, command);
29
+ }
30
+ };
31
+
10
32
  const run = async(commands, options, devextremeConfig) => {
11
33
  if(!commands[1]) {
12
34
  console.error('Command is incomplete. Please specify parameters.');
@@ -23,15 +45,15 @@ const run = async(commands, options, devextremeConfig) => {
23
45
  await angularApplication.create(appName, options);
24
46
  return;
25
47
  case 'react-app':
26
- await reactApplication.create(appName, options);
48
+ await createReact(appName, options, commands[0]);
27
49
  return;
28
50
  case 'vue-app':
29
51
  await vueApplication.create(appName, options);
30
52
  return;
31
53
  default:
32
- console.error(`The '${app}' application type is not valid`);
33
- printHelp(commands[0]);
54
+ handleWrongAppType(app, commands[0]);
34
55
  }
56
+
35
57
  } else {
36
58
  if(commands[0] === 'add') {
37
59
  if(commands[1] === 'devextreme-angular') {
@@ -40,7 +62,12 @@ const run = async(commands, options, devextremeConfig) => {
40
62
  }
41
63
 
42
64
  if(commands[1] === 'devextreme-react') {
43
- reactApplication.install(options);
65
+ if(nextjsApplication.isNextJsApp()) {
66
+ nextjsApplication.install(options);
67
+ } else {
68
+ reactApplication.install(options);
69
+ }
70
+
44
71
  return;
45
72
  }
46
73
 
@@ -54,23 +81,16 @@ const run = async(commands, options, devextremeConfig) => {
54
81
  return;
55
82
  }
56
83
 
57
- if(devextremeConfig.applicationEngine === 'angular') {
58
- if(commands[1] === 'view') {
59
- angularApplication.addView(commands[2], options);
60
- } else {
61
- console.error('Invalid command');
62
- printHelp(commands[0]);
63
- }
64
- } else if(devextremeConfig.applicationEngine === 'react') {
65
- if(commands[1] === 'view') {
66
- reactApplication.addView(commands[2], options);
67
- } else {
68
- console.error('Invalid command');
69
- printHelp(commands[0]);
70
- }
71
- } else if(devextremeConfig.applicationEngine === 'vue') {
84
+ const app = {
85
+ 'angular': angularApplication,
86
+ 'react': reactApplication,
87
+ 'nextjs': nextjsApplication,
88
+ 'vue': vueApplication,
89
+ }[devextremeConfig.applicationEngine];
90
+
91
+ if(app) {
72
92
  if(commands[1] === 'view') {
73
- vueApplication.addView(commands[2], options);
93
+ app.addView(commands[2], options);
74
94
  } else {
75
95
  console.error('Invalid command');
76
96
  printHelp(commands[0]);
@@ -7,10 +7,13 @@ const fs = require('fs');
7
7
  const dasherize = require('../utility/string').dasherize;
8
8
  const ngVersion = require('../utility/ng-version');
9
9
  const latestVersions = require('../utility/latest-versions');
10
- const { extractToolingVersion, toolingVersionOptionName } = require('../utility/extract-tooling-version');
10
+ const { extractDepsVersionTag, depsVersionTagOptionName } = require('../utility/extract-deps-version-tag');
11
+ const { getPackageJsonPath } = require('../utility/package-json-utils');
12
+ const modifyJson = require('../utility/modify-json-file');
11
13
  const schematicsVersion = latestVersions['devextreme-schematics'] || 'latest';
12
14
 
13
15
  const minNgCliVersion = new semver('17.0.0');
16
+ const ngCliWithZoneless = new semver('20.0.0');
14
17
 
15
18
  async function runSchematicCommand(schematicCommand, options, evaluatingOptions) {
16
19
  const collectionName = 'devextreme-schematics';
@@ -27,7 +30,7 @@ async function runSchematicCommand(schematicCommand, options, evaluatingOptions)
27
30
 
28
31
  const commandArguments = ['g', `${collectionName}:${schematicCommand}`];
29
32
 
30
- const { [toolingVersionOptionName]: _, ...optionsToArguments } = options; // eslint-disable-line no-unused-vars
33
+ const { [depsVersionTagOptionName]: _, ...optionsToArguments } = options; // eslint-disable-line no-unused-vars
31
34
  for(let option in optionsToArguments) {
32
35
  commandArguments.push(`--${dasherize(option)}=${options[option]}`);
33
36
  }
@@ -37,12 +40,13 @@ async function runSchematicCommand(schematicCommand, options, evaluatingOptions)
37
40
 
38
41
  async function runNgCommand(commandArguments, commandOptions, commandConfig) {
39
42
  const hasNg = await hasSutableNgCli();
40
- const toolingVersion = extractToolingVersion(commandOptions);
41
- const npmCommandName = hasNg && !toolingVersion ? 'ng' : 'npx';
43
+ const depsVersionTag = extractDepsVersionTag(commandOptions);
44
+ const npmCommandName = hasNg && !depsVersionTag ? 'ng' : 'npx';
42
45
  const [minCliLtsVersion] = minNgCliVersion.version.split('.');
43
- const ngCommandArguments = hasNg && !toolingVersion
46
+
47
+ const ngCommandArguments = hasNg && !depsVersionTag
44
48
  ? []
45
- : ['-p', `@angular/cli@v${minCliLtsVersion}-lts`, 'ng'];
49
+ : ['-p', `@angular/cli@${depsVersionTag || `v${minCliLtsVersion}-lts`}`, 'ng'];
46
50
 
47
51
  ngCommandArguments.push(...commandArguments);
48
52
  return runCommand(npmCommandName, ngCommandArguments, commandConfig);
@@ -60,6 +64,7 @@ function localPackageExists(packageName) {
60
64
 
61
65
  const hasSutableNgCli = async() => {
62
66
  const localVersion = ngVersion.getLocalNgVersion();
67
+
63
68
  if(!localVersion) {
64
69
  return false;
65
70
  }
@@ -74,8 +79,28 @@ const install = async(options) => {
74
79
  });
75
80
  };
76
81
 
82
+ const bumpAngular = (appPath, versionTag) => {
83
+ modifyJson(getPackageJsonPath(appPath), ({ dependencies, devDependencies, ...rest }) => {
84
+ const bump = (section) => {
85
+ for(const depName in section) {
86
+ section[depName] = depName.startsWith('@angular') ? versionTag : section[depName];
87
+ }
88
+ return section;
89
+ };
90
+
91
+ return {
92
+ dependencies: bump(dependencies),
93
+ devDependencies: bump(devDependencies),
94
+ ...rest,
95
+ };
96
+ });
97
+
98
+ };
99
+
77
100
  const create = async(appName, options) => {
78
101
  const layout = await getLayoutInfo(options.layout);
102
+ const depsVersionTag = extractDepsVersionTag(options);
103
+ const currentNgVersion = ngVersion.getNgCliVersion().version;
79
104
 
80
105
  const commandArguments = [
81
106
  'new',
@@ -84,14 +109,22 @@ const create = async(appName, options) => {
84
109
  '--routing=false',
85
110
  '--skip-tests=true',
86
111
  '--skip-install=true',
87
- '--standalone=false',
112
+ '--standalone=true',
88
113
  '--ssr=false'
89
114
  ];
90
115
 
116
+ if(ngCliWithZoneless.compare(currentNgVersion) <= 0) {
117
+ commandArguments.push('--zoneless=false');
118
+ }
119
+
91
120
  await runNgCommand(commandArguments, options);
92
121
 
93
122
  const appPath = path.join(process.cwd(), appName);
94
123
 
124
+ if(depsVersionTag) {
125
+ bumpAngular(appPath, depsVersionTag);
126
+ }
127
+
95
128
  options.resolveConflicts = 'override';
96
129
  options.updateBudgets = true;
97
130
  options.layout = layout;
@@ -125,9 +158,9 @@ const changeMainTs = (appPath) => {
125
158
  moduleWorker.insertImport(filePath, 'devextreme/ui/themes', 'themes', true);
126
159
 
127
160
  const fileContent = fs.readFileSync(filePath).toString();
128
- const bootstrapPattern = /platformBrowser(?:Dynamic)?\(\)\.bootstrapModule\(\s*AppModule\s*(?:,\s*\{[^}]*\})?\s*\)/;
161
+ const bootstrapPattern = /bootstrapApplication\([^)]+\)/;
129
162
  const firstChaptStr = fileContent.match(bootstrapPattern)[0];
130
- const lastChaptStr = '.catch(err => console.error(err));';
163
+ const lastChaptStr = '.catch((err) => console.error(err));';
131
164
 
132
165
  fs.writeFileSync(
133
166
  filePath,
@@ -0,0 +1,231 @@
1
+ const runCommand = require('../utility/run-command');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const getLayoutInfo = require('../utility/prompts/layout');
5
+ const getTemplateTypeInfo = require('../utility/prompts/typescript');
6
+ const templateCreator = require('../utility/template-creator');
7
+ const packageManager = require('../utility/package-manager');
8
+ const packageJsonUtils = require('../utility/package-json-utils');
9
+ const insertItemToArray = require('../utility/file-content').insertItemToArray;
10
+ const stringUtils = require('../utility/string');
11
+ const typescriptUtils = require('../utility/typescript-extension');
12
+ const removeFile = require('../utility/file-operations').remove;
13
+ const latestVersions = require('../utility/latest-versions');
14
+ const { extractDepsVersionTag } = require('../utility/extract-deps-version-tag');
15
+ const {
16
+ updateJsonPropName,
17
+ bumpReact,
18
+ getCorrectPath,
19
+ addStylesToApp,
20
+ getComponentPageName,
21
+ } = require('./application.react');
22
+
23
+ const defaultStyles = [
24
+ 'devextreme/dist/css/dx.light.css'
25
+ ];
26
+
27
+ const isNextJsApp = () => {
28
+ const appPath = process.cwd();
29
+
30
+ return fs.existsSync(path.join(appPath, 'next.config.ts')) || fs.existsSync(path.join(appPath, 'next.config.mjs'));
31
+ };
32
+
33
+ const isTsApp = (appPath) => {
34
+ return fs.existsSync(path.join(appPath, 'next.config.ts'));
35
+ };
36
+
37
+ const getExtension = (appPath) => {
38
+ return fs.existsSync(path.join(appPath, 'src/app', 'layout.tsx')) ? '.tsx' : '.jsx';
39
+ };
40
+
41
+ const pathToPagesIndex = () => {
42
+ const extension = getExtension(process.cwd());
43
+ return path.join(process.cwd(), 'src', 'views', `index${extension}`);
44
+ };
45
+
46
+ const preparePackageJsonForTemplate = (appPath, appName) => {
47
+ const dependencies = [
48
+ { name: 'devextreme-cli', version: latestVersions['devextreme-cli'], dev: true },
49
+ { name: 'jose', version: latestVersions['jose'] },
50
+ ];
51
+ const scripts = [
52
+ { name: 'build-themes', value: 'devextreme build' },
53
+ { name: 'postinstall', value: 'npm run build-themes' }
54
+ ];
55
+
56
+ packageJsonUtils.addDependencies(appPath, dependencies);
57
+ packageJsonUtils.updateScripts(appPath, scripts);
58
+ packageJsonUtils.updateName(appPath, appName);
59
+ };
60
+
61
+ const create = async(appName, options) => {
62
+ const templateType = await getTemplateTypeInfo(options.template);
63
+ const layoutType = await getLayoutInfo(options.layout);
64
+
65
+ const templateOptions = Object.assign({}, options, {
66
+ project: stringUtils.humanize(appName),
67
+ layout: stringUtils.classify(layoutType),
68
+ isTypeScript: typescriptUtils.isTypeScript(templateType)
69
+ });
70
+ const depsVersionTag = extractDepsVersionTag(options);
71
+
72
+ let commandArguments = [`-p=create-next-app@${depsVersionTag || latestVersions['create-next-app']}`, 'create-next-app', appName];
73
+
74
+ commandArguments = [
75
+ ...commandArguments,
76
+ `${templateOptions.isTypeScript ? '--typescript' : '--javascript'}`,
77
+ '--eslint',
78
+ '--no-tailwind',
79
+ '--src-dir',
80
+ '--app',
81
+ '--no-turbopack',
82
+ '--import-alias "@/*"',
83
+ ];
84
+
85
+ await runCommand('npx', commandArguments);
86
+
87
+ const appPath = path.join(process.cwd(), appName);
88
+
89
+ if(depsVersionTag) {
90
+ bumpReact(appPath, depsVersionTag, templateOptions.isTypeScript);
91
+ }
92
+
93
+ addTemplate(appPath, appName, templateOptions);
94
+ modifyAppFiles(appPath, templateOptions);
95
+ };
96
+
97
+ const modifyAppFiles = (appPath, { project, isTypeScript }) => {
98
+ const entryFilePath = path.join(appPath, `src/app/layout.${isTypeScript ? 'tsx' : 'jsx'}`);
99
+
100
+ let content = fs.readFileSync(entryFilePath).toString();
101
+ content = content.replace(/<title>[^<]+<\/title>/, `<title>${project}<\/title>`);
102
+
103
+ fs.writeFileSync(entryFilePath, content);
104
+ };
105
+
106
+ const addTemplate = (appPath, appName, templateOptions) => {
107
+ const applicationTemplatePath = path.join(
108
+ templateCreator.getTempaltePath('nextjs'),
109
+ 'application'
110
+ );
111
+
112
+ const manifestPath = path.join(appPath, 'public', 'manifest.json');
113
+
114
+ const styles = [
115
+ '../dx-styles.scss',
116
+ '../themes/generated/theme.additional.css',
117
+ '../themes/generated/theme.additional.dark.css',
118
+ '../themes/generated/theme.base.css',
119
+ '../themes/generated/theme.base.dark.css',
120
+ 'devextreme/dist/css/dx.common.css'
121
+ ];
122
+
123
+ templateCreator.moveTemplateFilesToProject(applicationTemplatePath, appPath, templateOptions, getCorrectPath);
124
+
125
+ !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.jsx'));
126
+ removeFile(path.join(appPath, 'src/app', 'page.js'));
127
+ removeFile(path.join(appPath, 'src/app', 'layout.js'));
128
+ removeFile(path.join(appPath, 'src/app', 'globals.scss'));
129
+
130
+ if(!templateOptions.empty) {
131
+ addSamplePages(appPath, templateOptions);
132
+ }
133
+
134
+ preparePackageJsonForTemplate(appPath, appName, templateOptions.isTypeScript);
135
+ updateJsonPropName(manifestPath, appName);
136
+ install({ isTypeScript: templateOptions.isTypeScript }, appPath, styles);
137
+ };
138
+
139
+ const getEntryFilePath = (options, appPath) => {
140
+ const extension = options.isTypeScript || isTsApp(appPath) ? 'ts' : 'js';
141
+ const srcFolder = fs.existsSync(path.join(appPath, 'src')) ? 'src' : '';
142
+ const isAppRouterApp = fs.existsSync(path.join(appPath, srcFolder, 'app')) && fs.lstatSync(appPath).isDirectory();
143
+
144
+ const entryFilePath = isAppRouterApp
145
+ ? path.join('app', `layout.${extension}`)
146
+ : path.join('pages', `_app.${extension}`);
147
+
148
+ const jsx = fs.existsSync(path.join(appPath, srcFolder, entryFilePath + 'x')) ? 'x' : '';
149
+
150
+ return path.join(srcFolder, entryFilePath + jsx);
151
+ };
152
+
153
+ const install = (options, appPath, styles) => {
154
+ appPath = appPath ? appPath : process.cwd();
155
+
156
+ const pathToMainComponent = path.join(appPath, getEntryFilePath(options, appPath));
157
+
158
+ addStylesToApp(pathToMainComponent, styles || defaultStyles);
159
+ packageJsonUtils.addDevextreme(appPath, options.dxversion, 'react');
160
+
161
+ packageManager.runInstall({ cwd: appPath });
162
+ };
163
+
164
+ const getNavigationData = (viewName, componentName, icon) => {
165
+ const pagePath = stringUtils.dasherize(viewName);
166
+ return {
167
+ navigation: `\n {\n text: \'${stringUtils.humanize(viewName)}\',\n path: \'/pages/${pagePath}\',\n icon: \'${icon}\'\n }`
168
+ };
169
+ };
170
+
171
+ const createPathToPage = (pageName) => {
172
+ const pagesPath = path.join(process.cwd(), 'src', 'app/pages');
173
+ const newPageFolderPath = path.join(pagesPath, pageName);
174
+
175
+ if(!fs.existsSync(pagesPath)) {
176
+ fs.mkdirSync(pagesPath);
177
+ fs.writeFileSync(pathToPagesIndex(), '');
178
+ }
179
+
180
+ if(!fs.existsSync(newPageFolderPath)) {
181
+ fs.mkdirSync(newPageFolderPath);
182
+ }
183
+
184
+ return newPageFolderPath;
185
+ };
186
+
187
+ const addSamplePages = (appPath, templateOptions) => {
188
+ const samplePageTemplatePath = path.join(
189
+ templateCreator.getTempaltePath('nextjs'),
190
+ 'sample-pages'
191
+ );
192
+
193
+ const pagesPath = path.join(appPath, 'src', 'app/pages');
194
+
195
+ templateCreator.moveTemplateFilesToProject(samplePageTemplatePath, pagesPath, {
196
+ isTypeScript: templateOptions.isTypeScript
197
+ }, getCorrectPath);
198
+ };
199
+
200
+ const addView = (pageName, options) => {
201
+ const pageTemplatePath = path.join(
202
+ templateCreator.getTempaltePath('nextjs'),
203
+ 'page'
204
+ );
205
+ const extension = getExtension(process.cwd());
206
+
207
+ const componentName = getComponentPageName(pageName);
208
+ const pathToPage = createPathToPage(pageName);
209
+ const navigationModulePath = path.join(process.cwd(), 'src', `app-navigation${extension}`);
210
+ const navigationData = getNavigationData(pageName, componentName, options && options.icon || 'folder');
211
+
212
+ const getCorrectExtension = (fileExtension) => {
213
+ return fileExtension === '.tsx' ? extension : fileExtension;
214
+ };
215
+
216
+ const getPageFileName = (pageName, pageItem) => {
217
+ return pageItem === 'page.tsx' ? 'page' : pageName;
218
+ };
219
+
220
+ templateCreator.addPageToApp(pageName, pathToPage, pageTemplatePath, getCorrectExtension, { getPageFileName });
221
+
222
+ insertItemToArray(navigationModulePath, navigationData.navigation);
223
+ };
224
+
225
+ module.exports = {
226
+ isNextJsApp,
227
+ install,
228
+ create,
229
+ addTemplate,
230
+ addView
231
+ };
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const fs = require('fs');
4
4
  const getLayoutInfo = require('../utility/prompts/layout');
5
5
  const getTemplateTypeInfo = require('../utility/prompts/typescript');
6
+ const getTranspilerTypeInfo = require('../utility/prompts/transpiler');
6
7
  const templateCreator = require('../utility/template-creator');
7
8
  const packageManager = require('../utility/package-manager');
8
9
  const packageJsonUtils = require('../utility/package-json-utils');
@@ -13,13 +14,13 @@ const stringUtils = require('../utility/string');
13
14
  const typescriptUtils = require('../utility/typescript-extension');
14
15
  const removeFile = require('../utility/file-operations').remove;
15
16
  const latestVersions = require('../utility/latest-versions');
16
- const { extractToolingVersion } = require('../utility/extract-tooling-version');
17
+ const { extractDepsVersionTag } = require('../utility/extract-deps-version-tag');
17
18
  const defaultStyles = [
18
19
  'devextreme/dist/css/dx.light.css'
19
20
  ];
20
21
 
21
22
  const getExtension = (appPath) => {
22
- return fs.existsSync(path.join(appPath, 'src', 'App.tsx')) ? '.tsx' : '.js';
23
+ return fs.existsSync(path.join(appPath, 'src', 'App.tsx')) ? '.tsx' : '.jsx';
23
24
  };
24
25
 
25
26
  const pathToPagesIndex = () => {
@@ -27,9 +28,9 @@ const pathToPagesIndex = () => {
27
28
  return path.join(process.cwd(), 'src', 'pages', `index${extension}`);
28
29
  };
29
30
 
30
- const preparePackageJsonForTemplate = (appPath, appName, isTypeScript) => {
31
+ const preparePackageJsonForTemplate = (appPath, appName) => {
31
32
  const dependencies = [
32
- { name: 'sass', version: '^1.34.1' },
33
+ { name: 'sass-embedded', version: '^1.85.1' },
33
34
  { name: 'devextreme-cli', version: latestVersions['devextreme-cli'], dev: true },
34
35
  { name: 'react-router-dom', version: '^6.3.0' },
35
36
  ];
@@ -51,8 +52,25 @@ const updateJsonPropName = (path, name) => {
51
52
  });
52
53
  };
53
54
 
55
+ const bumpReact = (appPath, versionTag, isTypeScript) => {
56
+ const dependencies = [
57
+ { name: 'react', version: versionTag },
58
+ { name: 'react-dom', version: versionTag },
59
+ ];
60
+
61
+ if(isTypeScript) {
62
+ dependencies.push(
63
+ { name: '@types/react', version: versionTag, dev: true },
64
+ { name: '@types/react-dom', version: versionTag, dev: true },
65
+ );
66
+ }
67
+
68
+ packageJsonUtils.addDependencies(appPath, dependencies);
69
+ };
70
+
54
71
  const create = async(appName, options) => {
55
72
  const templateType = await getTemplateTypeInfo(options.template);
73
+ const transpiler = await getTranspilerTypeInfo(options.transpiler);
56
74
  const layoutType = await getLayoutInfo(options.layout);
57
75
 
58
76
  const templateOptions = Object.assign({}, options, {
@@ -60,27 +78,30 @@ const create = async(appName, options) => {
60
78
  layout: stringUtils.classify(layoutType),
61
79
  isTypeScript: typescriptUtils.isTypeScript(templateType)
62
80
  });
63
- const toolingVersion = extractToolingVersion(options);
64
- const commandArguments = [`-p=create-react-app${toolingVersion}`, 'create-react-app', appName];
81
+ const depsVersionTag = extractDepsVersionTag(options);
65
82
 
66
- const templateSuffix = templateOptions.isTypeScript ? '-typescript' : '';
67
- const templatePath = path.resolve(__dirname, `../templates/cra-template${templateSuffix}`);
83
+ const commandArguments = [`-p=create-vite@${depsVersionTag || latestVersions['create-vite']}`, 'create-vite', appName];
68
84
 
69
- commandArguments.push(`--template file:${templatePath}`);
85
+ commandArguments.push(`--template react${transpiler === 'swc' ? '-swc' : ''}${templateOptions.isTypeScript ? '-ts' : ''}`);
70
86
 
71
87
  await runCommand('npx', commandArguments);
72
88
 
73
89
  const appPath = path.join(process.cwd(), appName);
74
90
 
75
91
  modifyIndexHtml(appPath, templateOptions.project);
92
+
93
+ if(depsVersionTag) {
94
+ bumpReact(appPath, depsVersionTag, templateOptions.isTypeScript);
95
+ }
96
+
76
97
  addTemplate(appPath, appName, templateOptions);
77
98
  };
78
99
 
79
100
  const modifyIndexHtml = (appPath, appName) => {
80
- const indexHtmlPath = path.join(appPath, 'public', 'index.html');
101
+ const indexHtmlPath = path.join(appPath, 'index.html');
81
102
 
82
103
  let htmlContent = fs.readFileSync(indexHtmlPath).toString();
83
- htmlContent = htmlContent.replace(/<title>(\w+\s*)+<\/title>/, `<title>${appName}<\/title>`);
104
+ htmlContent = htmlContent.replace(/<title>[^<]+<\/title>/, `<title>${appName}<\/title>`);
84
105
  htmlContent = htmlContent.replace('<body>', '<body class="dx-viewport">');
85
106
 
86
107
  fs.writeFileSync(indexHtmlPath, htmlContent);
@@ -97,7 +118,6 @@ const addTemplate = (appPath, appName, templateOptions) => {
97
118
  );
98
119
 
99
120
  const manifestPath = path.join(appPath, 'public', 'manifest.json');
100
- const indexPath = path.join(appPath, 'src', templateOptions.isTypeScript ? 'index.tsx' : 'index.js');
101
121
 
102
122
  const styles = [
103
123
  './themes/generated/theme.additional.css',
@@ -108,15 +128,15 @@ const addTemplate = (appPath, appName, templateOptions) => {
108
128
  ];
109
129
 
110
130
  templateCreator.moveTemplateFilesToProject(applicationTemplatePath, appPath, templateOptions, getCorrectPath);
111
- removeFile(path.join(appPath, 'src', 'App.css'));
112
- !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.js'));
131
+
132
+ !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.jsx'));
133
+
113
134
  if(!templateOptions.empty) {
114
135
  addSamplePages(appPath, templateOptions);
115
136
  }
116
137
 
117
138
  preparePackageJsonForTemplate(appPath, appName, templateOptions.isTypeScript);
118
139
  updateJsonPropName(manifestPath, appName);
119
- addPolyfills(packageJsonUtils.getPackageJsonPath(), indexPath);
120
140
  install({}, appPath, styles);
121
141
  };
122
142
 
@@ -130,15 +150,6 @@ const install = (options, appPath, styles) => {
130
150
  packageManager.runInstall({ cwd: appPath });
131
151
  };
132
152
 
133
- const addPolyfills = (packagePath, indexPath) => {
134
- const packages = [
135
- { name: 'react-app-polyfill', version: '^1.0.0' }
136
- ];
137
-
138
- packageJsonUtils.addDependencies(packagePath, packages);
139
- moduleUtils.insertImport(indexPath, './polyfills');
140
- };
141
-
142
153
  const addStylesToApp = (filePath, styles) => {
143
154
  styles.forEach(style => {
144
155
  moduleUtils.insertImport(filePath, style);
@@ -213,5 +224,10 @@ module.exports = {
213
224
  install,
214
225
  create,
215
226
  addTemplate,
216
- addView
227
+ addView,
228
+ updateJsonPropName,
229
+ bumpReact,
230
+ getCorrectPath,
231
+ addStylesToApp,
232
+ getComponentPageName,
217
233
  };