vk-typescript-cli 1.0.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 (119) hide show
  1. package/Jenkinsfile +48 -0
  2. package/README.md +300 -0
  3. package/bin/sf.js +438 -0
  4. package/eslint.config.js +90 -0
  5. package/gulpfile.js +157 -0
  6. package/lib/commander.js +3370 -0
  7. package/lib/inquirer-prompts.js +2038 -0
  8. package/license +13 -0
  9. package/package.json +43 -0
  10. package/smoke-test.mjs +38 -0
  11. package/src/commands/add.js +276 -0
  12. package/src/commands/info.js +82 -0
  13. package/src/commands/interactive.js +493 -0
  14. package/src/commands/mcp.js +135 -0
  15. package/src/commands/new.js +264 -0
  16. package/src/commands/skills.js +42 -0
  17. package/src/commands/theme.js +200 -0
  18. package/src/commands/upgrade.js +219 -0
  19. package/src/constants.js +161 -0
  20. package/src/generators/angular.js +878 -0
  21. package/src/generators/nextjs.js +817 -0
  22. package/src/generators/react.js +769 -0
  23. package/src/generators/typescript.js +258 -0
  24. package/src/generators/vue.js +800 -0
  25. package/src/templates/angular/chart.component.html +23 -0
  26. package/src/templates/angular/chart.component.ts +172 -0
  27. package/src/templates/angular/diagram.component.html +13 -0
  28. package/src/templates/angular/diagram.component.ts +224 -0
  29. package/src/templates/angular/docx-editor.component.html +18 -0
  30. package/src/templates/angular/docx-editor.component.ts +37 -0
  31. package/src/templates/angular/filemanager.component.html +4 -0
  32. package/src/templates/angular/filemanager.component.ts +47 -0
  33. package/src/templates/angular/gantt.component.html +32 -0
  34. package/src/templates/angular/gantt.component.ts +95 -0
  35. package/src/templates/angular/grid.component.html +12 -0
  36. package/src/templates/angular/grid.component.ts +58 -0
  37. package/src/templates/angular/pdf-viewer.component.html +9 -0
  38. package/src/templates/angular/pdf-viewer.component.ts +48 -0
  39. package/src/templates/angular/rte.component.html +7 -0
  40. package/src/templates/angular/rte.component.ts +13 -0
  41. package/src/templates/angular/scheduler.component.html +17 -0
  42. package/src/templates/angular/scheduler.component.ts +75 -0
  43. package/src/templates/angular/spreadsheet-editor.component.html +42 -0
  44. package/src/templates/angular/spreadsheet-editor.component.ts +61 -0
  45. package/src/templates/chart-template.js +20 -0
  46. package/src/templates/diagram-template.js +26 -0
  47. package/src/templates/docx-template.js +20 -0
  48. package/src/templates/filemanager-template.js +20 -0
  49. package/src/templates/gantt-template.js +20 -0
  50. package/src/templates/grid-template.js +20 -0
  51. package/src/templates/pdfviewer-template.js +20 -0
  52. package/src/templates/react/Chart.jsx +81 -0
  53. package/src/templates/react/Chart.tsx +81 -0
  54. package/src/templates/react/DOCXEditor.jsx +37 -0
  55. package/src/templates/react/DOCXEditor.tsx +37 -0
  56. package/src/templates/react/Diagram.jsx +192 -0
  57. package/src/templates/react/Diagram.tsx +195 -0
  58. package/src/templates/react/FileManager.jsx +13 -0
  59. package/src/templates/react/FileManager.tsx +13 -0
  60. package/src/templates/react/Gantt.jsx +89 -0
  61. package/src/templates/react/Gantt.tsx +89 -0
  62. package/src/templates/react/Grid.jsx +45 -0
  63. package/src/templates/react/Grid.tsx +45 -0
  64. package/src/templates/react/PDFViewer.jsx +16 -0
  65. package/src/templates/react/PDFViewer.tsx +16 -0
  66. package/src/templates/react/RTE.jsx +14 -0
  67. package/src/templates/react/RTE.tsx +14 -0
  68. package/src/templates/react/Scheduler.jsx +55 -0
  69. package/src/templates/react/Scheduler.tsx +55 -0
  70. package/src/templates/react/SpreadsheetEditor.jsx +96 -0
  71. package/src/templates/react/SpreadsheetEditor.tsx +98 -0
  72. package/src/templates/rte-template.js +26 -0
  73. package/src/templates/scheduler-template.js +20 -0
  74. package/src/templates/spreadheet-template.js +20 -0
  75. package/src/templates/typescript/RTE.ts +33 -0
  76. package/src/templates/typescript/SpreadsheetEditor.ts +39 -0
  77. package/src/templates/typescript/chart.ts +37 -0
  78. package/src/templates/typescript/diagram.ts +30 -0
  79. package/src/templates/typescript/docxeditor.ts +30 -0
  80. package/src/templates/typescript/filemanager.ts +41 -0
  81. package/src/templates/typescript/gantt.ts +88 -0
  82. package/src/templates/typescript/grid.ts +30 -0
  83. package/src/templates/typescript/pdfviewer.ts +8 -0
  84. package/src/templates/typescript/scheduler.ts +39 -0
  85. package/src/templates/vue/Chart.vue +257 -0
  86. package/src/templates/vue/ChartView.vue +7 -0
  87. package/src/templates/vue/DOCXEditor.vue +61 -0
  88. package/src/templates/vue/DOCXEditorView.vue +7 -0
  89. package/src/templates/vue/Diagram.vue +245 -0
  90. package/src/templates/vue/DiagramView.vue +7 -0
  91. package/src/templates/vue/FileManager.vue +58 -0
  92. package/src/templates/vue/FileManagerView.vue +7 -0
  93. package/src/templates/vue/Gantt.vue +129 -0
  94. package/src/templates/vue/GanttView.vue +7 -0
  95. package/src/templates/vue/Grid.vue +141 -0
  96. package/src/templates/vue/GridView.vue +7 -0
  97. package/src/templates/vue/PDFViewer.vue +58 -0
  98. package/src/templates/vue/PDFViewerView.vue +7 -0
  99. package/src/templates/vue/RTE.vue +27 -0
  100. package/src/templates/vue/RTEView.vue +7 -0
  101. package/src/templates/vue/Scheduler.vue +94 -0
  102. package/src/templates/vue/SchedulerView.vue +7 -0
  103. package/src/templates/vue/SpreadsheetEditor.vue +115 -0
  104. package/src/templates/vue/SpreadsheetEditorView.vue +7 -0
  105. package/src/utils/ansi-colors.js +38 -0
  106. package/src/utils/banner.js +179 -0
  107. package/src/utils/common.js +64 -0
  108. package/src/utils/config-generator.js +73 -0
  109. package/src/utils/folder-structure.js +26 -0
  110. package/src/utils/logger.js +19 -0
  111. package/src/utils/mcp-generator.js +122 -0
  112. package/src/utils/package-manager.js +128 -0
  113. package/src/utils/project-bootstrap.js +98 -0
  114. package/src/utils/project-detector.js +195 -0
  115. package/src/utils/prompt-utils.js +29 -0
  116. package/src/utils/run-command.js +110 -0
  117. package/src/utils/skills-installer.js +69 -0
  118. package/src/utils/template-file.js +35 -0
  119. package/src/utils/theme-loader.js +47 -0
package/bin/sf.js ADDED
@@ -0,0 +1,438 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { Command } from '../lib/commander.js';
7
+ import { createNewCommand } from '../src/commands/new.js';
8
+ import { interactiveMode } from '../src/commands/interactive.js';
9
+ import { addComponentCommand } from '../src/commands/add.js';
10
+ import { mcpAddCommand } from '../src/commands/mcp.js';
11
+ import { skillsAddCommand } from '../src/commands/skills.js';
12
+ // import { upgradeCommand } from '../src/commands/upgrade.js';
13
+ import { themeSwitchCommand } from '../src/commands/theme.js';
14
+ import { infoCommand } from '../src/commands/info.js';
15
+ import { logger } from '../src/utils/logger.js';
16
+ import { printBanner, printHelpFooter } from '../src/utils/banner.js';
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const pkg = JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf8'));
20
+ const CLI_VERSION = pkg.version;
21
+
22
+ const program = new Command();
23
+
24
+ program
25
+ .name('sf')
26
+ .description('CLI for creating and managing Syncfusion projects. Run without a command to start interactive mode.')
27
+ .usage('[command] [options]')
28
+ .version(CLI_VERSION, '-v, --version', 'Output the current CLI version')
29
+ .helpOption('-h, --help', 'Show CLI help');
30
+
31
+ const HELP_COMMAND_TERMS = {
32
+ new: 'sf new [appName] [framework] [options]',
33
+ add: 'sf add [component]',
34
+ mcp: 'sf mcp init [editor]',
35
+ skills: 'sf skills install',
36
+ theme: 'sf theme switch [themeName]',
37
+ // upgrade: 'sf upgrade [version]',
38
+ info: 'sf info'
39
+ };
40
+
41
+ // List of frameworks that the CLI knows how to scaffold. Used to validate
42
+ // the `--framework` flag in `sf new` and the positional `framework` argument.
43
+ const SUPPORTED_FRAMEWORKS = ['react', 'angular', 'vue', 'typescript'];
44
+
45
+ program.configureHelp({
46
+ sortSubcommands: false,
47
+ helpWidth: 120,
48
+ subcommandTerm: (cmd) => HELP_COMMAND_TERMS[cmd.name()] || `sf ${cmd.name()}`,
49
+ optionTerm: (option) => option.flags
50
+ });
51
+
52
+ // Branded banner shown above --help / help output.
53
+ program.addHelpText('afterAll', () => {
54
+ printHelpFooter();
55
+ return '';
56
+ });
57
+
58
+ program
59
+ .command('new [appName] [framework] [buildTool] [type] [template] [theme] [styling] [mcp] [skills] [start]')
60
+ .usage('[appName] [framework] [options]')
61
+ .description('Create a new Syncfusion project')
62
+ .option('--appname <appName>', 'Application name')
63
+ .option('--framework <framework>', 'Framework: react, angular, vue, typescript')
64
+ .option('--build-tool <buildTool>', 'Build tool for React only: vite, nextjs')
65
+ .option('--type <type>', 'Type: js, ts')
66
+ .option('--ts', 'Use TypeScript')
67
+ .option('--js', 'Use JavaScript')
68
+ .option('--template <template>', 'Template: empty, grid, chart, scheduler, gantt, file-manager, diagram, rich-text-editor, pdf-viewer, docx-editor, spreadsheet-editor')
69
+ .option('--theme <theme>', 'Theme: material3, fluent2, bootstrap5.3, tailwind3')
70
+ .option('--styling <styling>', 'Styling: css, scss')
71
+ .option('-y, --yes', 'Skip prompts and use defaults')
72
+ .option('--mcp <assistant>', 'Configure mcp for editor: all, vs-code, code-studio, cursor, jetbrains')
73
+ .option('--no-mcp', 'Do not configure the Syncfusion MCP integration')
74
+ .option('--skills', 'Install Syncfusion component skills')
75
+ .option('--no-skills', 'Do not install Syncfusion component skills')
76
+ .option('--start', 'Install dependencies and start the dev server')
77
+ .option('--no-start', 'Do not install dependencies/start the dev server')
78
+ .action(async (
79
+ appNameArg,
80
+ frameworkArg,
81
+ buildToolArg,
82
+ typeArg,
83
+ templateArg,
84
+ themeArg,
85
+ stylingArg,
86
+ mcpArg,
87
+ skillsArg,
88
+ startArg,
89
+ options
90
+ ) => {
91
+ try {
92
+ const normalize = (value) =>
93
+ typeof value === 'string' ? value.trim().toLowerCase() : value;
94
+
95
+ const resolveYesNo = (flagValue, positionalValue, defaultValue = true) => {
96
+ if (typeof flagValue === 'boolean') {
97
+ return flagValue;
98
+ }
99
+
100
+ if (positionalValue === undefined) {
101
+ return defaultValue;
102
+ }
103
+
104
+ const value = normalize(positionalValue);
105
+ const yesValues = ['yes', 'y', 'true', '1', 'enable', 'enabled', 'on'];
106
+ const noValues = ['no', 'n', 'false', '0', 'disable', 'disabled', 'off'];
107
+
108
+ if (yesValues.includes(value)) {
109
+ return true;
110
+ }
111
+
112
+ if (noValues.includes(value)) {
113
+ return false;
114
+ }
115
+
116
+ logger.error(`Invalid yes/no value: ${positionalValue}`);
117
+ logger.info('Use yes/no, true/false, y/n, 1/0, on/off, or enable/disable.');
118
+ process.exit(1);
119
+ };
120
+
121
+ const resolvemcp = (flagValue, positionalValue) => {
122
+ const rawValue = flagValue !== undefined ? flagValue : positionalValue;
123
+
124
+ if (rawValue === false || rawValue === undefined) {
125
+ return { mcp: false, mcpEditors: ['all'] };
126
+ }
127
+
128
+ const value = normalize(rawValue);
129
+ const yesValues = ['yes', 'y', 'true', '1', 'enable', 'enabled', 'on'];
130
+ const noValues = ['no', 'n', 'false', '0', 'disable', 'disabled', 'off'];
131
+ const editorValues = {
132
+ all: 'all',
133
+ vscode: 'vscode',
134
+ 'vs-code': 'vscode',
135
+ codestudio: 'codestudio',
136
+ 'code-studio': 'codestudio',
137
+ cursor: 'cursor',
138
+ jetbrains: 'jetbrains'
139
+ };
140
+
141
+ if (yesValues.includes(value)) {
142
+ return { mcp: true, mcpEditors: ['all'] };
143
+ }
144
+
145
+ if (noValues.includes(value)) {
146
+ return { mcp: false, mcpEditors: ['all'] };
147
+ }
148
+
149
+ if (editorValues[value]) {
150
+ return { mcp: true, mcpEditors: [editorValues[value]] };
151
+ }
152
+
153
+ logger.error(`Invalid mcp value: ${rawValue}`);
154
+ logger.info('Supported mcp values: all, vs-code, code-studio, cursor, jetbrains.');
155
+ process.exit(1);
156
+ };
157
+
158
+ const getDefaultType = (frameworkValue, buildToolValue) => {
159
+ const normalizedFramework = normalize(frameworkValue);
160
+ const normalizedBuildTool = normalize(buildToolValue);
161
+
162
+ if (normalizedFramework === 'vue') {
163
+ return 'js';
164
+ }
165
+
166
+ if (normalizedFramework === 'react' && normalizedBuildTool === 'vite') {
167
+ return 'js';
168
+ }
169
+
170
+ return 'ts';
171
+ };
172
+
173
+ let appName;
174
+ let framework;
175
+ let buildTool;
176
+ let type;
177
+ let template;
178
+ let theme;
179
+ let styling;
180
+ let mcpPosition;
181
+ let skillsPosition;
182
+ let startPosition;
183
+
184
+ if (options.appname) {
185
+ // Flag mode. Positional values are still allowed as fallback.
186
+ appName = options.appname;
187
+ framework = options.framework || frameworkArg || 'react';
188
+ framework = normalize(framework);
189
+
190
+ if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
191
+ logger.error(`Invalid framework: ${framework}`);
192
+ logger.info(`Supported frameworks: ${SUPPORTED_FRAMEWORKS.join(', ')}`);
193
+ process.exit(1);
194
+ }
195
+
196
+ if (framework === 'react') {
197
+ buildTool = options.buildTool || buildToolArg || 'vite';
198
+ type = options.type || typeArg || getDefaultType(framework, buildTool);
199
+ template = options.template || templateArg || 'empty';
200
+ theme = options.theme || themeArg || 'material3';
201
+ styling = options.styling || stylingArg || 'css';
202
+ mcpPosition = mcpArg;
203
+ skillsPosition = skillsArg;
204
+ startPosition = startArg;
205
+ } else {
206
+ // Build tool is not supported for Angular/Vue/TypeScript, so shift positional values left.
207
+ if (options.buildTool) {
208
+ logger.error('--build-tool is supported only for React projects');
209
+ process.exit(1);
210
+ }
211
+
212
+ buildTool = null;
213
+ type = options.type || typeArg || getDefaultType(framework, buildTool);
214
+ template = options.template || templateArg || 'empty';
215
+ theme = options.theme || themeArg || 'material3';
216
+ styling = options.styling || stylingArg || 'css';
217
+ mcpPosition = mcpArg;
218
+ skillsPosition = skillsArg;
219
+ startPosition = startArg;
220
+ }
221
+ } else {
222
+ // Positional mode.
223
+ appName = appNameArg;
224
+ framework = normalize(frameworkArg || options.framework || 'react');
225
+
226
+ if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
227
+ logger.error(`Invalid framework: ${framework}`);
228
+ logger.info(`Supported frameworks: ${SUPPORTED_FRAMEWORKS.join(', ')}`);
229
+ process.exit(1);
230
+ }
231
+
232
+ if (framework === 'react') {
233
+ buildTool = buildToolArg || options.buildTool || 'vite';
234
+ type = typeArg || options.type || getDefaultType(framework, buildTool);
235
+ template = templateArg || options.template || 'empty';
236
+ theme = themeArg || options.theme || 'material3';
237
+ styling = stylingArg || options.styling || 'css';
238
+ mcpPosition = mcpArg;
239
+ skillsPosition = skillsArg;
240
+ startPosition = startArg;
241
+ } else {
242
+ if (options.buildTool) {
243
+ logger.error('--build-tool is supported only for React projects');
244
+ process.exit(1);
245
+ }
246
+
247
+ buildTool = null;
248
+ type = buildToolArg || options.type || getDefaultType(framework, buildTool);
249
+ template = typeArg || options.template || 'empty';
250
+ theme = templateArg || options.theme || 'material3';
251
+ styling = themeArg || options.styling || 'css';
252
+ mcpPosition = stylingArg;
253
+ skillsPosition = mcpArg;
254
+ startPosition = skillsArg;
255
+ }
256
+ }
257
+
258
+ if (options.ts && options.js) {
259
+ logger.error('Use only one of --ts or --js');
260
+ process.exit(1);
261
+ }
262
+
263
+ if (options.ts) {
264
+ type = 'ts';
265
+ } else if (options.js) {
266
+ type = 'js';
267
+ }
268
+
269
+ if (!appName) {
270
+ logger.error('❌ Application name is required');
271
+ logger.info('Command usage: sf new [appname] [framework] [type] [template] [theme] [styling] [mcp] [skills]');
272
+ logger.info('Example: sf new my-app --framework react --theme tailwind3');
273
+ process.exit(1);
274
+ }
275
+
276
+ framework = normalize(framework);
277
+ type = normalize(type || getDefaultType(framework, buildTool));
278
+ template = normalize(template || 'empty');
279
+ theme = normalize(theme || 'material3');
280
+ styling = normalize(styling || 'css');
281
+
282
+ if (framework === 'typescript') {
283
+ // The TypeScript (vanilla EJ2) platform is always TypeScript and does
284
+ // not use a build tool.
285
+ buildTool = null;
286
+ type = 'ts';
287
+ }
288
+
289
+ if (framework === 'react') {
290
+ buildTool = normalize(buildTool || 'vite');
291
+ const validBuildTools = ['vite', 'nextjs'];
292
+
293
+ if (!validBuildTools.includes(buildTool)) {
294
+ logger.error(`Invalid build tool: ${buildTool}`);
295
+ logger.info(`Supported React build tools: ${validBuildTools.join(', ')}`);
296
+ process.exit(1);
297
+ }
298
+ } else {
299
+ buildTool = null;
300
+ }
301
+
302
+ const { mcp, mcpEditors } = resolvemcp(options.mcp, mcpPosition);
303
+ const skills = resolveYesNo(options.skills, skillsPosition, false);
304
+ const start = resolveYesNo(options.start, startPosition, false);
305
+
306
+ const commandOptions = {
307
+ framework,
308
+ buildTool,
309
+ type,
310
+ template,
311
+ theme,
312
+ styling,
313
+ yes: options.yes,
314
+ empty: options.empty,
315
+ mcp,
316
+ mcpEditors: mcpEditors.length > 0 ? mcpEditors : ['all'],
317
+ nomcp: !mcp,
318
+ skills,
319
+ noskills: !skills,
320
+ start,
321
+ noStart: !start
322
+ };
323
+
324
+ // Call the create command
325
+ await createNewCommand(commandOptions.framework, appName, commandOptions);
326
+
327
+ } catch (error) {
328
+ logger.error(`CLI Error: ${error.message}`);
329
+ process.exit(1);
330
+ }
331
+ });
332
+
333
+ // -----------------------------------------------------------------------------
334
+ // sf add <component> — add a Grid/Chart/Scheduler/Gantt/File Manager/Diagram/Rich Text Editor/PDF Viewer/DOCX Editor/Spreadsheet Editor
335
+ // -----------------------------------------------------------------------------
336
+ program
337
+ .command('add [component]')
338
+ .description('Add a component sample to the current project')
339
+ .action(async (component) => {
340
+ await addComponentCommand(component);
341
+ });
342
+
343
+ // -----------------------------------------------------------------------------
344
+ // sf mcp init [editor] — configure the Syncfusion mcp integration
345
+ // for this project. Supported editor values: all, vs-code, code-studio, cursor,
346
+ // jetbrains.
347
+ // -----------------------------------------------------------------------------
348
+ const mcpCommand = program
349
+ .command('mcp')
350
+ .description('Set up MCP (Model Context Protocol) for the current project.')
351
+ .action(() => {
352
+ logger.error('\n❌ Invalid command: sf mcp');
353
+ logger.info('\nUsage: sf mcp init [editor]');
354
+ logger.info('Example: sf mcp init code-studio\n');
355
+ });
356
+ mcpCommand
357
+ .command('init [editor]')
358
+ .description('Configure the Syncfusion mcp integration for your editor')
359
+ .action(async (editor, options) => {
360
+ await mcpAddCommand({...options,promptEditor: !editor}, editor);
361
+ });
362
+
363
+ // -----------------------------------------------------------------------------
364
+ // sf skills — install Syncfusion component skills for AI agents.
365
+ // -----------------------------------------------------------------------------
366
+ const skillsCommand = program
367
+ .command('skills')
368
+ .description('Add Syncfusion component Skills to the current project')
369
+ .action(() => {
370
+ logger.error('\n❌ Invalid command: sf skills');
371
+ logger.info('\nUsage: sf skills install');
372
+ });
373
+ skillsCommand
374
+ .command('install')
375
+ .description('Install Syncfusion component skills into the current project')
376
+ .action(async () => {
377
+ await skillsAddCommand({ promptInstallMode: true });
378
+ });
379
+
380
+ // -----------------------------------------------------------------------------
381
+ // sf theme switch [theme] — swap the active Syncfusion theme.
382
+ // -----------------------------------------------------------------------------
383
+ const themeCommand = program
384
+ .command('theme').description('Switch the Syncfusion theme used by the project')
385
+ .action(() => {
386
+ logger.error('\n❌ Invalid command: sf theme');
387
+ logger.info('\nUsage: sf theme switch <themeName>');
388
+ logger.info('Example: sf theme switch tailwind3\n');
389
+ });
390
+ themeCommand
391
+ .command('switch [themeName]')
392
+ .description('Switch the active theme: material3, tailwind3, bootstrap5.3, fluent2')
393
+ .action(async (theme) => {
394
+ await themeSwitchCommand(theme);
395
+ });
396
+
397
+ // -----------------------------------------------------------------------------
398
+ // upgrade commands
399
+ // -----------------------------------------------------------------------------
400
+ // program
401
+ // .command('upgrade [version]')
402
+ // .description('Upgrade installed @syncfusion packages to a specified version')
403
+ // .option('--reinstall', 'Delete node_modules/@syncfusion and reinstall upgraded packages')
404
+ // .option('--no-reinstall', 'Only update package.json and show manual reinstall steps')
405
+ // .action(async (version, options) => {
406
+ // await upgradeCommand({
407
+ // version,
408
+ // reinstallSyncfusionPackages: options.reinstall
409
+ // });
410
+ // });
411
+
412
+ program
413
+ .command('info')
414
+ .description('Show environment & project information')
415
+ .action(async () => {
416
+ await infoCommand();
417
+ });
418
+
419
+ program
420
+ .command('help')
421
+ .description('Show Syncfusion CLI help')
422
+ .action(() => {
423
+ program.help();
424
+ });
425
+
426
+ // Parse arguments
427
+ const args = process.argv.slice(2);
428
+
429
+ // If no arguments provided, run interactive mode
430
+ if (args.length === 0) {
431
+ printBanner(CLI_VERSION);
432
+ interactiveMode().catch((error) => {
433
+ logger.error(`Fatal error: ${error.message}`);
434
+ process.exit(1);
435
+ });
436
+ } else {
437
+ program.parse(process.argv);
438
+ }
@@ -0,0 +1,90 @@
1
+ import js from '@eslint/js';
2
+ import babelParser from '@babel/eslint-parser';
3
+
4
+ export default [
5
+ {
6
+ ignores: ['node_modules/', 'dist/', '.git/', 'build/', 'coverage/']
7
+ },
8
+ {
9
+ files: ['**/*.js'],
10
+ languageOptions: {
11
+ parser: babelParser,
12
+ parserOptions: {
13
+ ecmaVersion: 'latest',
14
+ sourceType: 'module',
15
+ requireConfigFile: false,
16
+ ecmaFeatures: {
17
+ impliedStrict: true
18
+ }
19
+ },
20
+ globals: {
21
+ console: 'readonly',
22
+ process: 'readonly',
23
+ Buffer: 'readonly',
24
+ __dirname: 'readonly',
25
+ __filename: 'readonly'
26
+ }
27
+ },
28
+ rules: {
29
+ ...js.configs.recommended.rules,
30
+ // Quality Rules
31
+ 'use-isnan': 'error',
32
+ 'no-constant-condition': 'error',
33
+ 'no-invalid-regexp': 'error',
34
+ 'no-control-regex': 'error',
35
+ 'eol-last': ['error', 'always'],
36
+ 'guard-for-in': 'error',
37
+ 'no-labels': 'error',
38
+ 'no-debugger': 'error',
39
+ 'no-eval': 'error',
40
+ 'no-extra-semi': 'error',
41
+ 'no-throw-literal': 'error',
42
+ 'no-fallthrough': 'error',
43
+ 'no-cond-assign': ['error', 'always'],
44
+ 'no-redeclare': ['error', { 'builtinGlobals': true }],
45
+ // Style Rules
46
+ 'semi': ['error', 'always'],
47
+ 'comma-dangle': ['error', 'never'],
48
+ 'no-trailing-spaces': 'error',
49
+ 'eqeqeq': ['error', 'smart'],
50
+ 'camelcase': ['error', {
51
+ 'properties': 'always',
52
+ 'ignoreDestructuring': true,
53
+ 'ignoreImports': true
54
+ }],
55
+ 'one-var': ['error', 'never'],
56
+ 'no-var': 'error',
57
+ 'radix': 'error',
58
+ // Type Safety
59
+ 'valid-typeof': ['error', { 'requireStringLiterals': true }],
60
+ // Whitespace
61
+ 'no-irregular-whitespace': ['error', {
62
+ 'skipStrings': true,
63
+ 'skipComments': true,
64
+ 'skipRegExps': true,
65
+ 'skipTemplates': true
66
+ }],
67
+ // Length & Console
68
+ 'max-len': ['error', {
69
+ 'code': 140,
70
+ 'tabWidth': 4,
71
+ 'ignoreComments': true,
72
+ 'ignoreStrings': true,
73
+ 'ignoreTemplateLiterals': true,
74
+ 'ignoreRegExpLiterals': true
75
+ }],
76
+ // Variables
77
+ 'no-unused-vars': ['error', {
78
+ 'args': 'after-used',
79
+ 'argsIgnorePattern': '^_',
80
+ 'varsIgnorePattern': '^_'
81
+ }],
82
+ 'no-undef': 'error',
83
+ 'no-import-assign': 'error',
84
+ 'no-dupe-keys': 'error',
85
+ 'no-empty': 'error',
86
+ // Warnings
87
+ 'prefer-const': 'warn'
88
+ }
89
+ }
90
+ ];
package/gulpfile.js ADDED
@@ -0,0 +1,157 @@
1
+ import gulp from 'gulp';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import shelljs from 'shelljs';
5
+ import { fileURLToPath } from 'url';
6
+ import { ESLint } from 'eslint';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const branchName = process.env.githubSourceBranch || '';
12
+ const isHotfix = /^hotfix\//g.test(branchName);
13
+ const isRelease = /^release\//g.test(branchName);
14
+
15
+ gulp.task('syncfusion-cli-publish', function (done) {
16
+ const packagePath = path.resolve(__dirname);
17
+ if (!fs.existsSync(packagePath)) {
18
+ console.log(`Package path does not exist: ${packagePath}`);
19
+ done();
20
+ return;
21
+ }
22
+ shelljs.cd(packagePath);
23
+ let nexusRepo = 'ej2-development';
24
+ if (isRelease) {
25
+ nexusRepo = 'ej2-release';
26
+ }
27
+ if (isHotfix) {
28
+ nexusRepo = 'ej2-hotfix-new';
29
+ }
30
+ const registryPath = `//nexus.syncfusioninternal.com/repository/${nexusRepo}/`;
31
+ const registryUrl = `https:${registryPath}`;
32
+ const npmrcContent = [
33
+ 'registry=https://registry.npmjs.org/',
34
+ `@syncfusion:registry=${registryUrl}`,
35
+ `${registryPath}:username=${process.env.PRIVATE_NPM_USER}`,
36
+ `${registryPath}:_password=${process.env.PRIVATE_NPM_PASSWORD}`,
37
+ `${registryPath}:email=${process.env.PRIVATE_NPM_EMAIL}`,
38
+ `${registryPath}:always-auth=true`
39
+ ].join('\n');
40
+
41
+ fs.writeFileSync('./.npmrc', npmrcContent);
42
+ const npmignoreContent = ['gulpfile.js', '.vscode/', '.npmignore', 'Jenkinsfile', 'tempFile', 'eslint.config.js'].join('\n');
43
+ fs.writeFileSync('./.npmignore', npmignoreContent);
44
+ const packageJsonPath = path.join(packagePath, 'package.json');
45
+ const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
46
+ const packageJson = JSON.parse(packageJsonContent);
47
+ if (packageJson.devDependencies) {
48
+ delete packageJson.devDependencies;
49
+ }
50
+ if (packageJson.scripts) {
51
+ delete packageJson.scripts.publish;
52
+ delete packageJson.scripts['ci-report'];
53
+ delete packageJson.scripts.test;
54
+ }
55
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
56
+ const publishResult = shelljs.exec('npm publish');
57
+ if (publishResult.code !== 0) {
58
+ done(new Error('npm publish failed'));
59
+ return;
60
+ }
61
+ done();
62
+ });
63
+
64
+ gulp.task('ci-report', function(done) {
65
+ console.log("Starting 'ci-compile'");
66
+ console.log("Finished 'ci-compile'");
67
+ done();
68
+ });
69
+
70
+ const colors = {
71
+ reset: '\x1b[0m',
72
+ green: '\x1b[32m',
73
+ red: '\x1b[31m',
74
+ yellow: '\x1b[33m',
75
+ blue: '\x1b[34m',
76
+ cyan: '\x1b[36m',
77
+ bold: '\x1b[1m'
78
+ };
79
+
80
+ const log = {
81
+ info: (msg) => console.log(`${colors.cyan}ℹ${colors.reset} ${msg}`),
82
+ success: (msg) => console.log(`${colors.green}✅ ${msg}${colors.reset}`),
83
+ error: (msg) => console.log(`${colors.red}❌ ${msg}${colors.reset}`),
84
+ warning: (msg) => console.log(`${colors.yellow}⚠️ ${msg}${colors.reset}`),
85
+ header: (msg) => console.log(`\n${colors.bold}${colors.blue}═══════════════════════════════════════${colors.reset}\n${colors.bold}${msg}${colors.reset}\n${colors.bold}${colors.blue}═══════════════════════════════════════${colors.reset}\n`),
86
+ section: (msg) => console.log(`\n${colors.bold}${colors.cyan}▶ ${msg}${colors.reset}`)
87
+ };
88
+
89
+ let totalTests = 0;
90
+ let passedTests = 0;
91
+ let failedTests = 0;
92
+
93
+ async function validateWithESLint() {
94
+ log.section('Test: ESLint Validation (Syntax, Unused Imports, Parse Errors)');
95
+
96
+ try {
97
+ const eslint = new ESLint({
98
+ cwd: __dirname
99
+ });
100
+ const filesToCheck = ['bin/sf.js', 'src/**/*.js'];
101
+ const results = await eslint.lintFiles(filesToCheck);
102
+ let eslintErrors = 0;
103
+ let eslintWarnings = 0;
104
+ for (const result of results) {
105
+ totalTests++;
106
+ const fileName = path.relative(__dirname, result.filePath);
107
+ if (result.messages.length === 0) {
108
+ log.success(`✓ ${fileName}`);
109
+ passedTests++;
110
+ } else {
111
+ let hasErrors = false;
112
+ for (const message of result.messages) {
113
+ if (message.severity === 2) {
114
+ log.error(`✗ ${fileName} - Line ${message.line}: ${message.message}`);
115
+ eslintErrors++;
116
+ hasErrors = true;
117
+ } else if (message.severity === 1) {
118
+ log.warning(`${fileName} - Line ${message.line}: ${message.message}`);
119
+ eslintWarnings++;
120
+ }
121
+ }
122
+ if (hasErrors) {
123
+ failedTests++;
124
+ }
125
+ }
126
+ }
127
+
128
+ return eslintErrors === 0;
129
+ } catch (error) {
130
+ log.error(`ESLint Error: ${error.message}`);
131
+ totalTests++;
132
+ failedTests++;
133
+ return false;
134
+ }
135
+ }
136
+
137
+ // GULP TASK
138
+ gulp.task('test', async function(done) {
139
+ totalTests = 0;
140
+ passedTests = 0;
141
+ failedTests = 0;
142
+ log.header('🧪 CLI VALIDATION TEST SUITE');
143
+ const eslintOk = await validateWithESLint();
144
+ log.header('📊 TEST SUMMARY');
145
+ console.log(`${colors.bold}Total Tests:${colors.reset} ${totalTests}`);
146
+ console.log(`${colors.green}${colors.bold}Passed:${colors.reset} ${passedTests}`);
147
+ console.log(`${colors.red}${colors.bold}Failed:${colors.reset} ${failedTests}`);
148
+ if (failedTests === 0) {
149
+ log.success('ALL TESTS PASSED! ✨');
150
+ console.log();
151
+ done();
152
+ } else {
153
+ log.error(`${failedTests} TEST(S) FAILED!`);
154
+ console.log();
155
+ done(new Error('Tests failed'));
156
+ }
157
+ });