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.
- package/Jenkinsfile +48 -0
- package/README.md +300 -0
- package/bin/sf.js +438 -0
- package/eslint.config.js +90 -0
- package/gulpfile.js +157 -0
- package/lib/commander.js +3370 -0
- package/lib/inquirer-prompts.js +2038 -0
- package/license +13 -0
- package/package.json +43 -0
- package/smoke-test.mjs +38 -0
- package/src/commands/add.js +276 -0
- package/src/commands/info.js +82 -0
- package/src/commands/interactive.js +493 -0
- package/src/commands/mcp.js +135 -0
- package/src/commands/new.js +264 -0
- package/src/commands/skills.js +42 -0
- package/src/commands/theme.js +200 -0
- package/src/commands/upgrade.js +219 -0
- package/src/constants.js +161 -0
- package/src/generators/angular.js +878 -0
- package/src/generators/nextjs.js +817 -0
- package/src/generators/react.js +769 -0
- package/src/generators/typescript.js +258 -0
- package/src/generators/vue.js +800 -0
- package/src/templates/angular/chart.component.html +23 -0
- package/src/templates/angular/chart.component.ts +172 -0
- package/src/templates/angular/diagram.component.html +13 -0
- package/src/templates/angular/diagram.component.ts +224 -0
- package/src/templates/angular/docx-editor.component.html +18 -0
- package/src/templates/angular/docx-editor.component.ts +37 -0
- package/src/templates/angular/filemanager.component.html +4 -0
- package/src/templates/angular/filemanager.component.ts +47 -0
- package/src/templates/angular/gantt.component.html +32 -0
- package/src/templates/angular/gantt.component.ts +95 -0
- package/src/templates/angular/grid.component.html +12 -0
- package/src/templates/angular/grid.component.ts +58 -0
- package/src/templates/angular/pdf-viewer.component.html +9 -0
- package/src/templates/angular/pdf-viewer.component.ts +48 -0
- package/src/templates/angular/rte.component.html +7 -0
- package/src/templates/angular/rte.component.ts +13 -0
- package/src/templates/angular/scheduler.component.html +17 -0
- package/src/templates/angular/scheduler.component.ts +75 -0
- package/src/templates/angular/spreadsheet-editor.component.html +42 -0
- package/src/templates/angular/spreadsheet-editor.component.ts +61 -0
- package/src/templates/chart-template.js +20 -0
- package/src/templates/diagram-template.js +26 -0
- package/src/templates/docx-template.js +20 -0
- package/src/templates/filemanager-template.js +20 -0
- package/src/templates/gantt-template.js +20 -0
- package/src/templates/grid-template.js +20 -0
- package/src/templates/pdfviewer-template.js +20 -0
- package/src/templates/react/Chart.jsx +81 -0
- package/src/templates/react/Chart.tsx +81 -0
- package/src/templates/react/DOCXEditor.jsx +37 -0
- package/src/templates/react/DOCXEditor.tsx +37 -0
- package/src/templates/react/Diagram.jsx +192 -0
- package/src/templates/react/Diagram.tsx +195 -0
- package/src/templates/react/FileManager.jsx +13 -0
- package/src/templates/react/FileManager.tsx +13 -0
- package/src/templates/react/Gantt.jsx +89 -0
- package/src/templates/react/Gantt.tsx +89 -0
- package/src/templates/react/Grid.jsx +45 -0
- package/src/templates/react/Grid.tsx +45 -0
- package/src/templates/react/PDFViewer.jsx +16 -0
- package/src/templates/react/PDFViewer.tsx +16 -0
- package/src/templates/react/RTE.jsx +14 -0
- package/src/templates/react/RTE.tsx +14 -0
- package/src/templates/react/Scheduler.jsx +55 -0
- package/src/templates/react/Scheduler.tsx +55 -0
- package/src/templates/react/SpreadsheetEditor.jsx +96 -0
- package/src/templates/react/SpreadsheetEditor.tsx +98 -0
- package/src/templates/rte-template.js +26 -0
- package/src/templates/scheduler-template.js +20 -0
- package/src/templates/spreadheet-template.js +20 -0
- package/src/templates/typescript/RTE.ts +33 -0
- package/src/templates/typescript/SpreadsheetEditor.ts +39 -0
- package/src/templates/typescript/chart.ts +37 -0
- package/src/templates/typescript/diagram.ts +30 -0
- package/src/templates/typescript/docxeditor.ts +30 -0
- package/src/templates/typescript/filemanager.ts +41 -0
- package/src/templates/typescript/gantt.ts +88 -0
- package/src/templates/typescript/grid.ts +30 -0
- package/src/templates/typescript/pdfviewer.ts +8 -0
- package/src/templates/typescript/scheduler.ts +39 -0
- package/src/templates/vue/Chart.vue +257 -0
- package/src/templates/vue/ChartView.vue +7 -0
- package/src/templates/vue/DOCXEditor.vue +61 -0
- package/src/templates/vue/DOCXEditorView.vue +7 -0
- package/src/templates/vue/Diagram.vue +245 -0
- package/src/templates/vue/DiagramView.vue +7 -0
- package/src/templates/vue/FileManager.vue +58 -0
- package/src/templates/vue/FileManagerView.vue +7 -0
- package/src/templates/vue/Gantt.vue +129 -0
- package/src/templates/vue/GanttView.vue +7 -0
- package/src/templates/vue/Grid.vue +141 -0
- package/src/templates/vue/GridView.vue +7 -0
- package/src/templates/vue/PDFViewer.vue +58 -0
- package/src/templates/vue/PDFViewerView.vue +7 -0
- package/src/templates/vue/RTE.vue +27 -0
- package/src/templates/vue/RTEView.vue +7 -0
- package/src/templates/vue/Scheduler.vue +94 -0
- package/src/templates/vue/SchedulerView.vue +7 -0
- package/src/templates/vue/SpreadsheetEditor.vue +115 -0
- package/src/templates/vue/SpreadsheetEditorView.vue +7 -0
- package/src/utils/ansi-colors.js +38 -0
- package/src/utils/banner.js +179 -0
- package/src/utils/common.js +64 -0
- package/src/utils/config-generator.js +73 -0
- package/src/utils/folder-structure.js +26 -0
- package/src/utils/logger.js +19 -0
- package/src/utils/mcp-generator.js +122 -0
- package/src/utils/package-manager.js +128 -0
- package/src/utils/project-bootstrap.js +98 -0
- package/src/utils/project-detector.js +195 -0
- package/src/utils/prompt-utils.js +29 -0
- package/src/utils/run-command.js +110 -0
- package/src/utils/skills-installer.js +69 -0
- package/src/utils/template-file.js +35 -0
- 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
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -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
|
+
});
|