@openwebf/webf 0.1.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/CLAUDE.md +206 -0
- package/README-zhCN.md +256 -0
- package/README.md +232 -0
- package/bin/webf.js +25 -0
- package/coverage/clover.xml +1295 -0
- package/coverage/coverage-final.json +12 -0
- package/coverage/lcov-report/IDLBlob.ts.html +142 -0
- package/coverage/lcov-report/analyzer.ts.html +2158 -0
- package/coverage/lcov-report/analyzer_original.ts.html +1450 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/commands.ts.html +700 -0
- package/coverage/lcov-report/dart.ts.html +490 -0
- package/coverage/lcov-report/declaration.ts.html +337 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/generator.ts.html +1171 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/logger.ts.html +424 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/react.ts.html +619 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/utils.ts.html +466 -0
- package/coverage/lcov-report/vue.ts.html +613 -0
- package/coverage/lcov.info +2149 -0
- package/global.d.ts +2 -0
- package/jest.config.js +24 -0
- package/package.json +36 -0
- package/src/IDLBlob.ts +20 -0
- package/src/analyzer.ts +692 -0
- package/src/commands.ts +645 -0
- package/src/dart.ts +170 -0
- package/src/declaration.ts +84 -0
- package/src/generator.ts +454 -0
- package/src/logger.ts +114 -0
- package/src/react.ts +186 -0
- package/src/utils.ts +127 -0
- package/src/vue.ts +176 -0
- package/templates/class.dart.tpl +86 -0
- package/templates/gitignore.tpl +2 -0
- package/templates/react.component.tsx.tpl +53 -0
- package/templates/react.createComponent.tpl +286 -0
- package/templates/react.index.ts.tpl +8 -0
- package/templates/react.package.json.tpl +26 -0
- package/templates/react.tsconfig.json.tpl +16 -0
- package/templates/react.tsup.config.ts.tpl +10 -0
- package/templates/tsconfig.json.tpl +8 -0
- package/templates/vue.component.partial.tpl +31 -0
- package/templates/vue.components.d.ts.tpl +49 -0
- package/templates/vue.package.json.tpl +11 -0
- package/templates/vue.tsconfig.json.tpl +15 -0
- package/test/IDLBlob.test.ts +75 -0
- package/test/analyzer.test.ts +370 -0
- package/test/commands.test.ts +1253 -0
- package/test/generator.test.ts +460 -0
- package/test/logger.test.ts +215 -0
- package/test/react.test.ts +49 -0
- package/test/utils.test.ts +316 -0
- package/tsconfig.json +30 -0
package/src/commands.ts
ADDED
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { dartGen, reactGen, vueGen } from './generator';
|
|
6
|
+
import _ from 'lodash';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import yaml from 'yaml';
|
|
9
|
+
|
|
10
|
+
interface GenerateOptions {
|
|
11
|
+
flutterPackageSrc?: string;
|
|
12
|
+
framework?: string;
|
|
13
|
+
packageName?: string;
|
|
14
|
+
publishToNpm?: boolean;
|
|
15
|
+
npmRegistry?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface FlutterPackageMetadata {
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const platform = process.platform;
|
|
25
|
+
const NPM = platform == 'win32' ? 'npm.cmd' : 'npm';
|
|
26
|
+
|
|
27
|
+
const gloabalDts = fs.readFileSync(
|
|
28
|
+
path.resolve(__dirname, '../global.d.ts'),
|
|
29
|
+
'utf-8'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const tsConfig = fs.readFileSync(
|
|
33
|
+
path.resolve(__dirname, '../templates/tsconfig.json.tpl'),
|
|
34
|
+
'utf-8'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const gitignore = fs.readFileSync(
|
|
38
|
+
path.resolve(__dirname, '../templates/gitignore.tpl'),
|
|
39
|
+
'utf-8'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const reactPackageJson = fs.readFileSync(
|
|
43
|
+
path.resolve(__dirname, '../templates/react.package.json.tpl'),
|
|
44
|
+
'utf-8'
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const reactTsConfig = fs.readFileSync(
|
|
48
|
+
path.resolve(__dirname, '../templates/react.tsconfig.json.tpl'),
|
|
49
|
+
'utf-8'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const reactTsUpConfig = fs.readFileSync(
|
|
53
|
+
path.resolve(__dirname, '../templates/react.tsup.config.ts.tpl'),
|
|
54
|
+
'utf-8'
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const createComponentTpl = fs.readFileSync(
|
|
58
|
+
path.resolve(__dirname, '../templates/react.createComponent.tpl'),
|
|
59
|
+
'utf-8'
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const reactIndexTpl = fs.readFileSync(
|
|
63
|
+
path.resolve(__dirname, '../templates/react.index.ts.tpl'),
|
|
64
|
+
'utf-8'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const vuePackageJson = fs.readFileSync(
|
|
68
|
+
path.resolve(__dirname, '../templates/vue.package.json.tpl'),
|
|
69
|
+
'utf-8'
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const vueTsConfig = fs.readFileSync(
|
|
73
|
+
path.resolve(__dirname, '../templates/vue.tsconfig.json.tpl'),
|
|
74
|
+
'utf-8'
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
function readFlutterPackageMetadata(packagePath: string): FlutterPackageMetadata | null {
|
|
78
|
+
try {
|
|
79
|
+
const pubspecPath = path.join(packagePath, 'pubspec.yaml');
|
|
80
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const pubspecContent = fs.readFileSync(pubspecPath, 'utf-8');
|
|
85
|
+
const pubspec = yaml.parse(pubspecContent);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
name: pubspec.name || '',
|
|
89
|
+
version: pubspec.version || '0.0.1',
|
|
90
|
+
description: pubspec.description || ''
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.warn('Warning: Could not read Flutter package metadata:', error);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function validateTypeScriptEnvironment(projectPath: string): { isValid: boolean; errors: string[] } {
|
|
99
|
+
const errors: string[] = [];
|
|
100
|
+
|
|
101
|
+
// Check for TypeScript configuration
|
|
102
|
+
const tsConfigPath = path.join(projectPath, 'tsconfig.json');
|
|
103
|
+
if (!fs.existsSync(tsConfigPath)) {
|
|
104
|
+
errors.push('Missing tsconfig.json - TypeScript configuration is required for type definitions');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check for .d.ts files - this is critical
|
|
108
|
+
const libPath = path.join(projectPath, 'lib');
|
|
109
|
+
let hasDtsFiles = false;
|
|
110
|
+
|
|
111
|
+
if (fs.existsSync(libPath)) {
|
|
112
|
+
// Check in lib directory
|
|
113
|
+
hasDtsFiles = fs.readdirSync(libPath).some(file =>
|
|
114
|
+
file.endsWith('.d.ts') ||
|
|
115
|
+
(fs.statSync(path.join(libPath, file)).isDirectory() &&
|
|
116
|
+
fs.readdirSync(path.join(libPath, file)).some(f => f.endsWith('.d.ts')))
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Also check in root directory
|
|
121
|
+
if (!hasDtsFiles) {
|
|
122
|
+
hasDtsFiles = fs.readdirSync(projectPath).some(file =>
|
|
123
|
+
file.endsWith('.d.ts') ||
|
|
124
|
+
(fs.statSync(path.join(projectPath, file)).isDirectory() &&
|
|
125
|
+
file !== 'node_modules' &&
|
|
126
|
+
fs.existsSync(path.join(projectPath, file, 'index.d.ts')))
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!hasDtsFiles) {
|
|
131
|
+
errors.push('No TypeScript definition files (.d.ts) found in the project - Please create .d.ts files for your components');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
isValid: errors.length === 0,
|
|
136
|
+
errors
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function createCommand(target: string, options: { framework: string; packageName: string; metadata?: FlutterPackageMetadata }): void {
|
|
141
|
+
const { framework, packageName, metadata } = options;
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(target)) {
|
|
144
|
+
fs.mkdirSync(target, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (framework === 'react') {
|
|
148
|
+
const packageJsonPath = path.join(target, 'package.json');
|
|
149
|
+
const packageJsonContent = _.template(reactPackageJson)({
|
|
150
|
+
packageName,
|
|
151
|
+
version: metadata?.version || '0.0.1',
|
|
152
|
+
description: metadata?.description || ''
|
|
153
|
+
});
|
|
154
|
+
writeFileIfChanged(packageJsonPath, packageJsonContent);
|
|
155
|
+
|
|
156
|
+
const tsConfigPath = path.join(target, 'tsconfig.json');
|
|
157
|
+
const tsConfigContent = _.template(reactTsConfig)({});
|
|
158
|
+
writeFileIfChanged(tsConfigPath, tsConfigContent);
|
|
159
|
+
|
|
160
|
+
const tsupConfigPath = path.join(target, 'tsup.config.ts');
|
|
161
|
+
const tsupConfigContent = _.template(reactTsUpConfig)({});
|
|
162
|
+
writeFileIfChanged(tsupConfigPath, tsupConfigContent);
|
|
163
|
+
|
|
164
|
+
const gitignorePath = path.join(target, '.gitignore');
|
|
165
|
+
const gitignoreContent = _.template(gitignore)({});
|
|
166
|
+
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
167
|
+
|
|
168
|
+
const srcDir = path.join(target, 'src');
|
|
169
|
+
if (!fs.existsSync(srcDir)) {
|
|
170
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const indexFilePath = path.join(srcDir, 'index.ts');
|
|
174
|
+
const indexContent = _.template(reactIndexTpl)({
|
|
175
|
+
components: [],
|
|
176
|
+
});
|
|
177
|
+
writeFileIfChanged(indexFilePath, indexContent);
|
|
178
|
+
|
|
179
|
+
const utilsDir = path.join(srcDir, 'utils');
|
|
180
|
+
if (!fs.existsSync(utilsDir)) {
|
|
181
|
+
fs.mkdirSync(utilsDir, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const createComponentPath = path.join(utilsDir, 'createComponent.ts');
|
|
185
|
+
const createComponentContent = _.template(createComponentTpl)({});
|
|
186
|
+
writeFileIfChanged(createComponentPath, createComponentContent);
|
|
187
|
+
|
|
188
|
+
spawnSync(NPM, ['install', '--omit=peer'], {
|
|
189
|
+
cwd: target,
|
|
190
|
+
stdio: 'inherit'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
} else if (framework === 'vue') {
|
|
194
|
+
const packageJsonPath = path.join(target, 'package.json');
|
|
195
|
+
const packageJsonContent = _.template(vuePackageJson)({
|
|
196
|
+
packageName,
|
|
197
|
+
version: metadata?.version || '0.0.1',
|
|
198
|
+
description: metadata?.description || ''
|
|
199
|
+
});
|
|
200
|
+
writeFileIfChanged(packageJsonPath, packageJsonContent);
|
|
201
|
+
|
|
202
|
+
const tsConfigPath = path.join(target, 'tsconfig.json');
|
|
203
|
+
const tsConfigContent = _.template(vueTsConfig)({});
|
|
204
|
+
writeFileIfChanged(tsConfigPath, tsConfigContent);
|
|
205
|
+
|
|
206
|
+
const gitignorePath = path.join(target, '.gitignore');
|
|
207
|
+
const gitignoreContent = _.template(gitignore)({});
|
|
208
|
+
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
209
|
+
|
|
210
|
+
spawnSync(NPM, ['install', '@openwebf/webf-enterprise-typings'], {
|
|
211
|
+
cwd: target,
|
|
212
|
+
stdio: 'inherit'
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
spawnSync(NPM, ['install', '@types/vue', '-D'], {
|
|
216
|
+
cwd: target,
|
|
217
|
+
stdio: 'inherit'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(`WebF ${framework} package created at: ${target}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function generateCommand(distPath: string, options: GenerateOptions): Promise<void> {
|
|
225
|
+
// If distPath is not provided or is '.', create a temporary directory
|
|
226
|
+
let resolvedDistPath: string;
|
|
227
|
+
let isTempDir = false;
|
|
228
|
+
|
|
229
|
+
if (!distPath || distPath === '.') {
|
|
230
|
+
// Create a temporary directory for the generated package
|
|
231
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webf-typings-'));
|
|
232
|
+
resolvedDistPath = tempDir;
|
|
233
|
+
isTempDir = true;
|
|
234
|
+
console.log(`\nUsing temporary directory: ${tempDir}`);
|
|
235
|
+
} else {
|
|
236
|
+
resolvedDistPath = path.resolve(distPath);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// First, check if we're in a Flutter package directory when flutter-package-src is not provided
|
|
240
|
+
if (!options.flutterPackageSrc) {
|
|
241
|
+
// Check if current directory or parent directories contain pubspec.yaml
|
|
242
|
+
let currentDir = process.cwd();
|
|
243
|
+
let foundPubspec = false;
|
|
244
|
+
let pubspecDir = '';
|
|
245
|
+
|
|
246
|
+
// Search up to 3 levels up for pubspec.yaml
|
|
247
|
+
for (let i = 0; i < 3; i++) {
|
|
248
|
+
const pubspecPath = path.join(currentDir, 'pubspec.yaml');
|
|
249
|
+
if (fs.existsSync(pubspecPath)) {
|
|
250
|
+
foundPubspec = true;
|
|
251
|
+
pubspecDir = currentDir;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
const parentDir = path.dirname(currentDir);
|
|
255
|
+
if (parentDir === currentDir) break; // Reached root
|
|
256
|
+
currentDir = parentDir;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (foundPubspec) {
|
|
260
|
+
// Use the directory containing pubspec.yaml as the flutter package source
|
|
261
|
+
options.flutterPackageSrc = pubspecDir;
|
|
262
|
+
console.log(`\nDetected Flutter package at: ${pubspecDir}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Check if the directory exists and has required files
|
|
267
|
+
const packageJsonPath = path.join(resolvedDistPath, 'package.json');
|
|
268
|
+
const globalDtsPath = path.join(resolvedDistPath, 'global.d.ts');
|
|
269
|
+
const tsConfigPath = path.join(resolvedDistPath, 'tsconfig.json');
|
|
270
|
+
|
|
271
|
+
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
272
|
+
const hasGlobalDts = fs.existsSync(globalDtsPath);
|
|
273
|
+
const hasTsConfig = fs.existsSync(tsConfigPath);
|
|
274
|
+
|
|
275
|
+
// Determine if we need to create a new project
|
|
276
|
+
const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
|
|
277
|
+
|
|
278
|
+
let framework = options.framework;
|
|
279
|
+
let packageName = options.packageName;
|
|
280
|
+
|
|
281
|
+
if (needsProjectCreation) {
|
|
282
|
+
// If project needs creation but options are missing, prompt for them
|
|
283
|
+
if (!framework) {
|
|
284
|
+
const frameworkAnswer = await inquirer.prompt([{
|
|
285
|
+
type: 'list',
|
|
286
|
+
name: 'framework',
|
|
287
|
+
message: 'Which framework would you like to use?',
|
|
288
|
+
choices: ['react', 'vue']
|
|
289
|
+
}]);
|
|
290
|
+
framework = frameworkAnswer.framework;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Try to read Flutter package metadata if flutterPackageSrc is provided
|
|
294
|
+
let metadata: FlutterPackageMetadata | null = null;
|
|
295
|
+
if (options.flutterPackageSrc) {
|
|
296
|
+
metadata = readFlutterPackageMetadata(options.flutterPackageSrc);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!packageName) {
|
|
300
|
+
// Use Flutter package name as default if available
|
|
301
|
+
const defaultPackageName = metadata?.name || path.basename(resolvedDistPath);
|
|
302
|
+
|
|
303
|
+
const packageNameAnswer = await inquirer.prompt([{
|
|
304
|
+
type: 'input',
|
|
305
|
+
name: 'packageName',
|
|
306
|
+
message: 'What is your package name?',
|
|
307
|
+
default: defaultPackageName,
|
|
308
|
+
validate: (input: string) => {
|
|
309
|
+
if (!input || input.trim() === '') {
|
|
310
|
+
return 'Package name is required';
|
|
311
|
+
}
|
|
312
|
+
// Basic npm package name validation
|
|
313
|
+
if (!/^[a-z0-9]([a-z0-9-._])*$/.test(input)) {
|
|
314
|
+
return 'Package name must be lowercase and may contain hyphens, dots, and underscores';
|
|
315
|
+
}
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
}]);
|
|
319
|
+
packageName = packageNameAnswer.packageName;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log(`\nCreating new ${framework} project in ${resolvedDistPath}...`);
|
|
323
|
+
createCommand(resolvedDistPath, {
|
|
324
|
+
framework: framework!,
|
|
325
|
+
packageName: packageName!,
|
|
326
|
+
metadata: metadata || undefined
|
|
327
|
+
});
|
|
328
|
+
} else {
|
|
329
|
+
// Validate existing project structure
|
|
330
|
+
if (hasPackageJson) {
|
|
331
|
+
try {
|
|
332
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
333
|
+
|
|
334
|
+
// Detect framework from existing package.json
|
|
335
|
+
if (!framework) {
|
|
336
|
+
if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
|
|
337
|
+
framework = 'react';
|
|
338
|
+
} else if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
|
|
339
|
+
framework = 'vue';
|
|
340
|
+
} else {
|
|
341
|
+
// If can't detect, prompt for it
|
|
342
|
+
const frameworkAnswer = await inquirer.prompt([{
|
|
343
|
+
type: 'list',
|
|
344
|
+
name: 'framework',
|
|
345
|
+
message: 'Which framework are you using?',
|
|
346
|
+
choices: ['react', 'vue']
|
|
347
|
+
}]);
|
|
348
|
+
framework = frameworkAnswer.framework;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.error('Error reading package.json:', e);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Now proceed with code generation if flutter package source is provided
|
|
361
|
+
if (!options.flutterPackageSrc) {
|
|
362
|
+
console.log('\nProject is ready for code generation.');
|
|
363
|
+
console.log('To generate code, run:');
|
|
364
|
+
const displayPath = isTempDir ? '<output-dir>' : distPath;
|
|
365
|
+
console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --framework=${framework}`);
|
|
366
|
+
if (isTempDir) {
|
|
367
|
+
// Clean up temporary directory if we're not using it
|
|
368
|
+
fs.rmSync(resolvedDistPath, { recursive: true, force: true });
|
|
369
|
+
}
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Validate TypeScript environment in the Flutter package
|
|
374
|
+
console.log(`\nValidating TypeScript environment in ${options.flutterPackageSrc}...`);
|
|
375
|
+
const validation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
376
|
+
|
|
377
|
+
if (!validation.isValid) {
|
|
378
|
+
// Check specifically for missing tsconfig.json
|
|
379
|
+
const tsConfigPath = path.join(options.flutterPackageSrc, 'tsconfig.json');
|
|
380
|
+
if (!fs.existsSync(tsConfigPath)) {
|
|
381
|
+
const createTsConfigAnswer = await inquirer.prompt([{
|
|
382
|
+
type: 'confirm',
|
|
383
|
+
name: 'createTsConfig',
|
|
384
|
+
message: 'No tsconfig.json found. Would you like me to create one for you?',
|
|
385
|
+
default: true
|
|
386
|
+
}]);
|
|
387
|
+
|
|
388
|
+
if (createTsConfigAnswer.createTsConfig) {
|
|
389
|
+
// Create a default tsconfig.json
|
|
390
|
+
const defaultTsConfig = {
|
|
391
|
+
compilerOptions: {
|
|
392
|
+
target: 'ES2020',
|
|
393
|
+
module: 'commonjs',
|
|
394
|
+
lib: ['ES2020'],
|
|
395
|
+
declaration: true,
|
|
396
|
+
strict: true,
|
|
397
|
+
esModuleInterop: true,
|
|
398
|
+
skipLibCheck: true,
|
|
399
|
+
forceConsistentCasingInFileNames: true,
|
|
400
|
+
resolveJsonModule: true,
|
|
401
|
+
moduleResolution: 'node'
|
|
402
|
+
},
|
|
403
|
+
include: ['lib/**/*.d.ts', '**/*.d.ts'],
|
|
404
|
+
exclude: ['node_modules', 'dist', 'build']
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
fs.writeFileSync(tsConfigPath, JSON.stringify(defaultTsConfig, null, 2), 'utf-8');
|
|
408
|
+
console.log('ā
Created tsconfig.json');
|
|
409
|
+
|
|
410
|
+
// Re-validate after creating tsconfig
|
|
411
|
+
const newValidation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
412
|
+
if (!newValidation.isValid) {
|
|
413
|
+
console.error('\nā ļø Additional setup required:');
|
|
414
|
+
newValidation.errors.forEach(error => console.error(` - ${error}`));
|
|
415
|
+
console.error('\nPlease fix the above issues and run the command again.');
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
console.error('\nā TypeScript configuration is required for code generation.');
|
|
420
|
+
console.error('Please create a tsconfig.json file manually and run the command again.');
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
} else {
|
|
424
|
+
// Show all validation errors
|
|
425
|
+
console.error('\nā TypeScript environment validation failed:');
|
|
426
|
+
validation.errors.forEach(error => console.error(` - ${error}`));
|
|
427
|
+
console.error('\nPlease fix the above issues before generating code.');
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const command = `webf codegen --flutter-package-src=${options.flutterPackageSrc} --framework=${framework} <distPath>`;
|
|
433
|
+
|
|
434
|
+
// Auto-initialize typings in the output directory if needed
|
|
435
|
+
ensureInitialized(resolvedDistPath);
|
|
436
|
+
|
|
437
|
+
console.log(`\nGenerating ${framework} code from ${options.flutterPackageSrc}...`);
|
|
438
|
+
|
|
439
|
+
await dartGen({
|
|
440
|
+
source: options.flutterPackageSrc,
|
|
441
|
+
target: options.flutterPackageSrc,
|
|
442
|
+
command,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
if (framework === 'react') {
|
|
446
|
+
await reactGen({
|
|
447
|
+
source: options.flutterPackageSrc,
|
|
448
|
+
target: resolvedDistPath,
|
|
449
|
+
command,
|
|
450
|
+
});
|
|
451
|
+
} else if (framework === 'vue') {
|
|
452
|
+
await vueGen({
|
|
453
|
+
source: options.flutterPackageSrc,
|
|
454
|
+
target: resolvedDistPath,
|
|
455
|
+
command,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
console.log('\nCode generation completed successfully!');
|
|
460
|
+
|
|
461
|
+
// Automatically build the generated package
|
|
462
|
+
if (framework) {
|
|
463
|
+
try {
|
|
464
|
+
await buildPackage(resolvedDistPath);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
console.error('\nWarning: Build failed:', error);
|
|
467
|
+
// Don't exit here since generation was successful
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Handle npm publishing if requested via command line option
|
|
472
|
+
if (options.publishToNpm && framework) {
|
|
473
|
+
try {
|
|
474
|
+
await buildAndPublishPackage(resolvedDistPath, options.npmRegistry);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.error('\nError during npm publish:', error);
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
} else if (framework && !options.publishToNpm) {
|
|
480
|
+
// If not publishing via command line option, ask the user
|
|
481
|
+
const publishAnswer = await inquirer.prompt([{
|
|
482
|
+
type: 'confirm',
|
|
483
|
+
name: 'publish',
|
|
484
|
+
message: 'Would you like to publish this package to npm?',
|
|
485
|
+
default: false
|
|
486
|
+
}]);
|
|
487
|
+
|
|
488
|
+
if (publishAnswer.publish) {
|
|
489
|
+
// Ask for registry
|
|
490
|
+
const registryAnswer = await inquirer.prompt([{
|
|
491
|
+
type: 'input',
|
|
492
|
+
name: 'registry',
|
|
493
|
+
message: 'NPM registry URL (leave empty for default npm registry):',
|
|
494
|
+
default: '',
|
|
495
|
+
validate: (input: string) => {
|
|
496
|
+
if (!input) return true; // Empty is valid (use default)
|
|
497
|
+
try {
|
|
498
|
+
new URL(input); // Validate URL format
|
|
499
|
+
return true;
|
|
500
|
+
} catch {
|
|
501
|
+
return 'Please enter a valid URL';
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}]);
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
await buildAndPublishPackage(
|
|
508
|
+
resolvedDistPath,
|
|
509
|
+
registryAnswer.registry || undefined
|
|
510
|
+
);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.error('\nError during npm publish:', error);
|
|
513
|
+
// Don't exit here since generation was successful
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// If using a temporary directory, remind the user where the files are
|
|
519
|
+
if (isTempDir) {
|
|
520
|
+
console.log(`\nš Generated files are in: ${resolvedDistPath}`);
|
|
521
|
+
console.log('š” To use these files, copy them to your desired location or publish to npm.');
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function writeFileIfChanged(filePath: string, content: string) {
|
|
526
|
+
if (fs.existsSync(filePath)) {
|
|
527
|
+
const oldContent = fs.readFileSync(filePath, 'utf-8')
|
|
528
|
+
if (oldContent === content) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function ensureInitialized(targetPath: string): void {
|
|
537
|
+
const globalDtsPath = path.join(targetPath, 'global.d.ts');
|
|
538
|
+
const tsConfigPath = path.join(targetPath, 'tsconfig.json');
|
|
539
|
+
|
|
540
|
+
// Check if initialization files already exist
|
|
541
|
+
const needsInit = !fs.existsSync(globalDtsPath) || !fs.existsSync(tsConfigPath);
|
|
542
|
+
|
|
543
|
+
if (needsInit) {
|
|
544
|
+
console.log('Initializing WebF typings...');
|
|
545
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
546
|
+
|
|
547
|
+
if (!fs.existsSync(globalDtsPath)) {
|
|
548
|
+
fs.writeFileSync(globalDtsPath, gloabalDts, 'utf-8');
|
|
549
|
+
console.log('Created global.d.ts');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (!fs.existsSync(tsConfigPath)) {
|
|
553
|
+
fs.writeFileSync(tsConfigPath, tsConfig, 'utf-8');
|
|
554
|
+
console.log('Created tsconfig.json');
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async function buildPackage(packagePath: string): Promise<void> {
|
|
560
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
561
|
+
|
|
562
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
563
|
+
throw new Error(`No package.json found in ${packagePath}`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
567
|
+
const packageName = packageJson.name;
|
|
568
|
+
const packageVersion = packageJson.version;
|
|
569
|
+
|
|
570
|
+
// Check if package has a build script
|
|
571
|
+
if (packageJson.scripts?.build) {
|
|
572
|
+
console.log(`\nBuilding ${packageName}@${packageVersion}...`);
|
|
573
|
+
const buildResult = spawnSync(NPM, ['run', 'build'], {
|
|
574
|
+
cwd: packagePath,
|
|
575
|
+
stdio: 'inherit'
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
if (buildResult.status !== 0) {
|
|
579
|
+
throw new Error('Build failed');
|
|
580
|
+
}
|
|
581
|
+
console.log('ā
Build completed successfully!');
|
|
582
|
+
} else {
|
|
583
|
+
console.log(`\nNo build script found for ${packageName}@${packageVersion}`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
async function buildAndPublishPackage(packagePath: string, registry?: string): Promise<void> {
|
|
588
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
589
|
+
|
|
590
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
591
|
+
throw new Error(`No package.json found in ${packagePath}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
595
|
+
const packageName = packageJson.name;
|
|
596
|
+
const packageVersion = packageJson.version;
|
|
597
|
+
|
|
598
|
+
// Set registry if provided
|
|
599
|
+
if (registry) {
|
|
600
|
+
console.log(`\nUsing npm registry: ${registry}`);
|
|
601
|
+
const setRegistryResult = spawnSync(NPM, ['config', 'set', 'registry', registry], {
|
|
602
|
+
cwd: packagePath,
|
|
603
|
+
stdio: 'inherit'
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
if (setRegistryResult.status !== 0) {
|
|
607
|
+
throw new Error('Failed to set npm registry');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Check if user is logged in to npm
|
|
612
|
+
const whoamiResult = spawnSync(NPM, ['whoami'], {
|
|
613
|
+
cwd: packagePath,
|
|
614
|
+
encoding: 'utf-8'
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
if (whoamiResult.status !== 0) {
|
|
618
|
+
console.error('\nError: You must be logged in to npm to publish packages.');
|
|
619
|
+
console.error('Please run "npm login" first.');
|
|
620
|
+
throw new Error('Not logged in to npm');
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
console.log(`\nPublishing ${packageName}@${packageVersion} to npm...`);
|
|
624
|
+
|
|
625
|
+
// Publish the package
|
|
626
|
+
const publishResult = spawnSync(NPM, ['publish'], {
|
|
627
|
+
cwd: packagePath,
|
|
628
|
+
stdio: 'inherit'
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
if (publishResult.status !== 0) {
|
|
632
|
+
throw new Error('Publish failed');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
console.log(`\nā
Successfully published ${packageName}@${packageVersion}`);
|
|
636
|
+
|
|
637
|
+
// Reset registry to default if it was changed
|
|
638
|
+
if (registry) {
|
|
639
|
+
spawnSync(NPM, ['config', 'delete', 'registry'], {
|
|
640
|
+
cwd: packagePath
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export { generateCommand };
|