@openwebf/webf 0.22.3 → 0.22.5
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/dist/IDLBlob.js +17 -0
- package/dist/analyzer.js +578 -0
- package/dist/analyzer_original.js +467 -0
- package/dist/commands.js +704 -0
- package/dist/dart.js +300 -0
- package/dist/declaration.js +63 -0
- package/dist/generator.js +466 -0
- package/dist/logger.js +103 -0
- package/dist/react.js +283 -0
- package/dist/utils.js +127 -0
- package/dist/vue.js +159 -0
- package/package.json +11 -1
- package/src/dart.ts +138 -7
- package/templates/class.dart.tpl +7 -2
- package/templates/react.package.json.tpl +1 -1
- package/CLAUDE.md +0 -206
- package/README-zhCN.md +0 -256
- package/coverage/clover.xml +0 -1295
- package/coverage/coverage-final.json +0 -12
- package/coverage/lcov-report/IDLBlob.ts.html +0 -142
- package/coverage/lcov-report/analyzer.ts.html +0 -2158
- package/coverage/lcov-report/analyzer_original.ts.html +0 -1450
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/commands.ts.html +0 -700
- package/coverage/lcov-report/dart.ts.html +0 -490
- package/coverage/lcov-report/declaration.ts.html +0 -337
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/generator.ts.html +0 -1171
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/logger.ts.html +0 -424
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/react.ts.html +0 -619
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/utils.ts.html +0 -466
- package/coverage/lcov-report/vue.ts.html +0 -613
- package/coverage/lcov.info +0 -2149
- package/jest.config.js +0 -24
package/src/dart.ts
CHANGED
|
@@ -2,7 +2,7 @@ import _ from "lodash";
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import {ParameterType} from "./analyzer";
|
|
5
|
-
import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject} from "./declaration";
|
|
5
|
+
import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject, PropsDeclaration} from "./declaration";
|
|
6
6
|
import {IDLBlob} from "./IDLBlob";
|
|
7
7
|
import {getPointerType, isPointerType} from "./utils";
|
|
8
8
|
|
|
@@ -10,7 +10,79 @@ function readTemplate(name: string) {
|
|
|
10
10
|
return fs.readFileSync(path.join(__dirname, '../templates/' + name + '.tpl'), {encoding: 'utf-8'});
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Generate enum name from property name
|
|
14
|
+
function getEnumName(className: string, propName: string): string {
|
|
15
|
+
// Remove 'Properties' or 'Bindings' suffix from className
|
|
16
|
+
const baseName = className.replace(/Properties$|Bindings$/, '');
|
|
17
|
+
// Convert to PascalCase
|
|
18
|
+
return baseName + _.upperFirst(_.camelCase(propName));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if a type is a union of string literals
|
|
22
|
+
function isStringUnionType(type: ParameterType): boolean {
|
|
23
|
+
if (!Array.isArray(type.value)) return false;
|
|
24
|
+
|
|
25
|
+
// For now, we'll consider any union type as potentially a string union
|
|
26
|
+
// and let getUnionStringValues determine if it actually contains string literals
|
|
27
|
+
return type.value.length > 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Extract string literal values from union type
|
|
31
|
+
function getUnionStringValues(prop: PropsDeclaration, blob: IDLBlob): string[] | null {
|
|
32
|
+
if (!isStringUnionType(prop.type)) return null;
|
|
33
|
+
|
|
34
|
+
// Try to get the actual string values from the source TypeScript file
|
|
35
|
+
const sourceContent = blob.raw;
|
|
36
|
+
if (!sourceContent) return null;
|
|
37
|
+
|
|
38
|
+
// Look for the property definition in the source
|
|
39
|
+
// Need to escape special characters in property names (like value-color)
|
|
40
|
+
const escapedPropName = prop.name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
41
|
+
const propPattern = new RegExp(`['"]?${escapedPropName}['"]?\\s*\\?\\s*:\\s*([^;]+);`);
|
|
42
|
+
const match = sourceContent.match(propPattern);
|
|
43
|
+
if (!match) return null;
|
|
44
|
+
|
|
45
|
+
// Extract string literals from union type
|
|
46
|
+
const unionType = match[1];
|
|
47
|
+
const literalPattern = /'([^']+)'|"([^"]+)"/g;
|
|
48
|
+
const values: string[] = [];
|
|
49
|
+
let literalMatch;
|
|
50
|
+
|
|
51
|
+
while ((literalMatch = literalPattern.exec(unionType)) !== null) {
|
|
52
|
+
values.push(literalMatch[1] || literalMatch[2]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return values.length > 0 ? values : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Generate Dart enum from string values
|
|
59
|
+
function generateDartEnum(enumName: string, values: string[]): string {
|
|
60
|
+
const enumValues = values.map(value => {
|
|
61
|
+
// Convert kebab-case to camelCase for enum values
|
|
62
|
+
const enumValue = _.camelCase(value);
|
|
63
|
+
return ` ${enumValue}('${value}')`;
|
|
64
|
+
}).join(',\n');
|
|
65
|
+
|
|
66
|
+
return `enum ${enumName} {
|
|
67
|
+
${enumValues};
|
|
68
|
+
|
|
69
|
+
final String value;
|
|
70
|
+
const ${enumName}(this.value);
|
|
71
|
+
|
|
72
|
+
static ${enumName}? parse(String? value) {
|
|
73
|
+
if (value == null) return null;
|
|
74
|
+
return ${enumName}.values.firstWhere(
|
|
75
|
+
(e) => e.value == value,
|
|
76
|
+
orElse: () => throw ArgumentError('Invalid ${enumName} value: $value'),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@override
|
|
81
|
+
String toString() => value;
|
|
82
|
+
}`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function generateReturnType(type: ParameterType, enumName?: string) {
|
|
14
86
|
if (isPointerType(type)) {
|
|
15
87
|
const pointerType = getPointerType(type);
|
|
16
88
|
return pointerType;
|
|
@@ -18,6 +90,19 @@ function generateReturnType(type: ParameterType) {
|
|
|
18
90
|
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
19
91
|
return `${getPointerType(type.value)}[]`;
|
|
20
92
|
}
|
|
93
|
+
|
|
94
|
+
// Handle union types (e.g., 'left' | 'center' | 'right')
|
|
95
|
+
if (Array.isArray(type.value)) {
|
|
96
|
+
// If we have an enum name, use it; otherwise fall back to String
|
|
97
|
+
return enumName || 'String';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Handle when type.value is a ParameterType object (nested type)
|
|
101
|
+
if (typeof type.value === 'object' && !Array.isArray(type.value) && type.value !== null) {
|
|
102
|
+
// This might be a nested ParameterType, recurse
|
|
103
|
+
return generateReturnType(type.value as ParameterType, enumName);
|
|
104
|
+
}
|
|
105
|
+
|
|
21
106
|
switch (type.value) {
|
|
22
107
|
case FunctionArgumentType.int: {
|
|
23
108
|
return 'int';
|
|
@@ -55,8 +140,14 @@ function generateEventHandlerType(type: ParameterType) {
|
|
|
55
140
|
throw new Error('Unknown event type: ' + pointerType);
|
|
56
141
|
}
|
|
57
142
|
|
|
58
|
-
function generateAttributeSetter(propName: string, type: ParameterType): string {
|
|
143
|
+
function generateAttributeSetter(propName: string, type: ParameterType, enumName?: string): string {
|
|
59
144
|
// Attributes from HTML are always strings, so we need to convert them
|
|
145
|
+
|
|
146
|
+
// Handle enum types
|
|
147
|
+
if (enumName && Array.isArray(type.value)) {
|
|
148
|
+
return `${propName} = ${enumName}.parse(value)`;
|
|
149
|
+
}
|
|
150
|
+
|
|
60
151
|
switch (type.value) {
|
|
61
152
|
case FunctionArgumentType.boolean:
|
|
62
153
|
return `${propName} = value == 'true' || value == ''`;
|
|
@@ -70,7 +161,12 @@ function generateAttributeSetter(propName: string, type: ParameterType): string
|
|
|
70
161
|
}
|
|
71
162
|
}
|
|
72
163
|
|
|
73
|
-
function generateAttributeGetter(propName: string, type: ParameterType, optional: boolean): string {
|
|
164
|
+
function generateAttributeGetter(propName: string, type: ParameterType, optional: boolean, enumName?: string): string {
|
|
165
|
+
// Handle enum types
|
|
166
|
+
if (enumName && Array.isArray(type.value)) {
|
|
167
|
+
return optional ? `${propName}?.value` : `${propName}.value`;
|
|
168
|
+
}
|
|
169
|
+
|
|
74
170
|
// Handle nullable properties - they should return null if the value is null
|
|
75
171
|
if (optional && type.value !== FunctionArgumentType.boolean) {
|
|
76
172
|
// For nullable properties, we need to handle null values properly
|
|
@@ -127,6 +223,9 @@ function shouldMakeNullable(prop: any): boolean {
|
|
|
127
223
|
return prop.optional;
|
|
128
224
|
}
|
|
129
225
|
|
|
226
|
+
// Export for testing
|
|
227
|
+
export { isStringUnionType, getUnionStringValues };
|
|
228
|
+
|
|
130
229
|
export function generateDartClass(blob: IDLBlob, command: string): string {
|
|
131
230
|
const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
|
|
132
231
|
const classObjectDictionary = Object.fromEntries(
|
|
@@ -176,6 +275,26 @@ interface ${object.name} {
|
|
|
176
275
|
if (!className) {
|
|
177
276
|
return '';
|
|
178
277
|
}
|
|
278
|
+
|
|
279
|
+
// Generate enums for union types
|
|
280
|
+
const enums: { name: string; definition: string }[] = [];
|
|
281
|
+
const enumMap: Map<string, string> = new Map(); // prop name -> enum name
|
|
282
|
+
|
|
283
|
+
if (componentProperties) {
|
|
284
|
+
for (const prop of componentProperties.props) {
|
|
285
|
+
if (isStringUnionType(prop.type)) {
|
|
286
|
+
const values = getUnionStringValues(prop, blob);
|
|
287
|
+
if (values && values.length > 0) {
|
|
288
|
+
const enumName = getEnumName(componentProperties.name, prop.name);
|
|
289
|
+
enums.push({
|
|
290
|
+
name: enumName,
|
|
291
|
+
definition: generateDartEnum(enumName, values)
|
|
292
|
+
});
|
|
293
|
+
enumMap.set(prop.name, enumName);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
179
298
|
|
|
180
299
|
const content = _.template(readTemplate('class.dart'))({
|
|
181
300
|
className: className,
|
|
@@ -184,14 +303,26 @@ interface ${object.name} {
|
|
|
184
303
|
classObjectDictionary,
|
|
185
304
|
dependencies,
|
|
186
305
|
blob,
|
|
187
|
-
generateReturnType,
|
|
306
|
+
generateReturnType: (type: ParameterType, propName?: string) => {
|
|
307
|
+
// If we have a prop name, check if it has an enum
|
|
308
|
+
if (propName && enumMap.has(propName)) {
|
|
309
|
+
return enumMap.get(propName)!;
|
|
310
|
+
}
|
|
311
|
+
return generateReturnType(type);
|
|
312
|
+
},
|
|
188
313
|
generateMethodDeclaration,
|
|
189
314
|
generateEventHandlerType,
|
|
190
|
-
generateAttributeSetter,
|
|
191
|
-
|
|
315
|
+
generateAttributeSetter: (propName: string, type: ParameterType) => {
|
|
316
|
+
return generateAttributeSetter(propName, type, enumMap.get(propName));
|
|
317
|
+
},
|
|
318
|
+
generateAttributeGetter: (propName: string, type: ParameterType, optional: boolean) => {
|
|
319
|
+
return generateAttributeGetter(propName, type, optional, enumMap.get(propName));
|
|
320
|
+
},
|
|
192
321
|
generateAttributeDeleter,
|
|
193
322
|
shouldMakeNullable,
|
|
194
323
|
command,
|
|
324
|
+
enums,
|
|
325
|
+
enumMap,
|
|
195
326
|
});
|
|
196
327
|
|
|
197
328
|
return content.split('\n').filter(str => {
|
package/templates/class.dart.tpl
CHANGED
|
@@ -11,15 +11,20 @@
|
|
|
11
11
|
|
|
12
12
|
import 'package:webf/webf.dart';
|
|
13
13
|
|
|
14
|
+
<% _.forEach(enums, function(enumDef) { %>
|
|
15
|
+
<%= enumDef.definition %>
|
|
16
|
+
|
|
17
|
+
<% }); %>
|
|
18
|
+
|
|
14
19
|
abstract class <%= className %>Bindings extends WidgetElement {
|
|
15
20
|
<%= className %>Bindings(super.context);
|
|
16
21
|
|
|
17
22
|
<% _.forEach(properties?.props, function(prop, index) { %>
|
|
18
23
|
<% var propName = _.camelCase(prop.name); %>
|
|
19
24
|
<% if (shouldMakeNullable(prop)) { %>
|
|
20
|
-
<%= generateReturnType(prop.type) %>? get <%= propName %>;
|
|
25
|
+
<%= generateReturnType(prop.type, propName) %>? get <%= propName %>;
|
|
21
26
|
<% } else { %>
|
|
22
|
-
<%= generateReturnType(prop.type) %> get <%= propName %>;
|
|
27
|
+
<%= generateReturnType(prop.type, propName) %> get <%= propName %>;
|
|
23
28
|
<% } %>
|
|
24
29
|
set <%= propName %>(value);
|
|
25
30
|
<% }); %>
|
package/CLAUDE.md
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# WebF CLI Development Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
The WebF CLI is a code generation tool that creates type-safe bindings between Flutter/Dart and JavaScript frameworks (React, Vue). It analyzes TypeScript definition files and generates corresponding Dart classes and JavaScript/TypeScript components.
|
|
5
|
-
|
|
6
|
-
## Architecture
|
|
7
|
-
|
|
8
|
-
### Core Components
|
|
9
|
-
- `analyzer.ts` - TypeScript AST analysis with multi-level caching
|
|
10
|
-
- `generator.ts` - Orchestrates code generation for Dart, React, and Vue
|
|
11
|
-
- `dart.ts` - Dart code generation from TypeScript definitions
|
|
12
|
-
- `react.ts` - React component generation
|
|
13
|
-
- `vue.ts` - Vue component generation
|
|
14
|
-
- `commands.ts` - CLI command implementations
|
|
15
|
-
- `logger.ts` - Logging utility without external dependencies
|
|
16
|
-
|
|
17
|
-
### Key Features
|
|
18
|
-
- Multi-level caching for performance optimization
|
|
19
|
-
- Parallel file processing
|
|
20
|
-
- Type-safe attribute handling with automatic conversions
|
|
21
|
-
- Comprehensive error handling and recovery
|
|
22
|
-
|
|
23
|
-
## Code Generation Patterns
|
|
24
|
-
|
|
25
|
-
### TypeScript to Dart Type Mapping
|
|
26
|
-
```typescript
|
|
27
|
-
// Boolean attributes are always non-nullable in Dart
|
|
28
|
-
interface Props {
|
|
29
|
-
open?: boolean; // Generates: bool get open;
|
|
30
|
-
title?: string; // Generates: String? get title;
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Attribute Type Conversion
|
|
35
|
-
HTML attributes are always strings, so the generator includes automatic type conversion:
|
|
36
|
-
- Boolean: `value == 'true' || value == ''`
|
|
37
|
-
- Integer: `int.tryParse(value) ?? 0`
|
|
38
|
-
- Double: `double.tryParse(value) ?? 0.0`
|
|
39
|
-
- String: Direct assignment
|
|
40
|
-
|
|
41
|
-
## Performance Optimizations
|
|
42
|
-
|
|
43
|
-
### Caching Strategy
|
|
44
|
-
1. **Source File Cache**: Parsed TypeScript AST files are cached
|
|
45
|
-
2. **Type Conversion Cache**: Frequently used type conversions are cached
|
|
46
|
-
3. **File Content Cache**: File contents are cached to detect changes
|
|
47
|
-
|
|
48
|
-
### Batch Processing
|
|
49
|
-
Files are processed in batches for optimal parallelism:
|
|
50
|
-
```typescript
|
|
51
|
-
await processFilesInBatch(items, batchSize, processor);
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Testing Guidelines
|
|
55
|
-
|
|
56
|
-
### Test Structure
|
|
57
|
-
- Unit tests for all core modules
|
|
58
|
-
- Mock file system operations before module imports
|
|
59
|
-
- Test coverage threshold: 70%
|
|
60
|
-
|
|
61
|
-
### Running Tests
|
|
62
|
-
```bash
|
|
63
|
-
npm test # Run all tests
|
|
64
|
-
npm test -- test/analyzer.test.ts # Run specific test
|
|
65
|
-
npm run test:coverage # Run with coverage report
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Mock Patterns
|
|
69
|
-
For modules that read files at load time:
|
|
70
|
-
```typescript
|
|
71
|
-
jest.mock('fs');
|
|
72
|
-
import fs from 'fs';
|
|
73
|
-
const mockFs = fs as jest.Mocked<typeof fs>;
|
|
74
|
-
mockFs.readFileSync = jest.fn().mockImplementation((path) => {
|
|
75
|
-
// Return appropriate content
|
|
76
|
-
});
|
|
77
|
-
// Now import the module
|
|
78
|
-
import { moduleUnderTest } from './module';
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## CLI Usage
|
|
82
|
-
|
|
83
|
-
### Commands
|
|
84
|
-
```bash
|
|
85
|
-
# Generate code from TypeScript definitions (auto-creates project if needed)
|
|
86
|
-
webf codegen <output-dir> --flutter-package-src=<path> [--framework=react|vue] [--package-name=<name>] [--publish-to-npm] [--npm-registry=<url>]
|
|
87
|
-
|
|
88
|
-
# Create a new project without code generation
|
|
89
|
-
webf codegen <output-dir> [--framework=react|vue] [--package-name=<name>]
|
|
90
|
-
|
|
91
|
-
# Generate and publish to npm
|
|
92
|
-
webf codegen <output-dir> --flutter-package-src=<path> --publish-to-npm
|
|
93
|
-
|
|
94
|
-
# Generate and publish to custom registry
|
|
95
|
-
webf codegen <output-dir> --flutter-package-src=<path> --publish-to-npm --npm-registry=https://custom.registry.com/
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Auto-creation Behavior
|
|
99
|
-
The `generate` command now automatically detects if a project needs to be created:
|
|
100
|
-
- If required files (package.json, global.d.ts, tsconfig.json) are missing, it will create a new project
|
|
101
|
-
- If framework or package name are not provided, it will prompt interactively
|
|
102
|
-
- If an existing project is detected, it will use the existing configuration
|
|
103
|
-
- Framework can be auto-detected from existing package.json dependencies
|
|
104
|
-
|
|
105
|
-
### Metadata Synchronization
|
|
106
|
-
When creating typing projects, the CLI automatically synchronizes metadata from the Flutter package:
|
|
107
|
-
- Reads `pubspec.yaml` from the Flutter package source directory
|
|
108
|
-
- Extracts version and description information
|
|
109
|
-
- Applies this metadata to the generated `package.json` files
|
|
110
|
-
- Ensures typing packages match the same version as the Flutter package
|
|
111
|
-
|
|
112
|
-
### Automatic Build
|
|
113
|
-
After code generation, the CLI automatically runs `npm run build` if a build script is present in the package.json. This ensures the generated package is immediately ready for use or publishing. The build process:
|
|
114
|
-
- Checks for the presence of a `build` script in package.json
|
|
115
|
-
- Runs the build command if available
|
|
116
|
-
- Continues successfully even if the build fails (with a warning)
|
|
117
|
-
- Provides clear console output about the build status
|
|
118
|
-
|
|
119
|
-
### NPM Publishing
|
|
120
|
-
The CLI supports automatic npm publishing with the following features:
|
|
121
|
-
- **--publish-to-npm**: Automatically publishes the generated package to npm (build is run automatically)
|
|
122
|
-
- **--npm-registry**: Specify a custom npm registry URL (defaults to https://registry.npmjs.org/)
|
|
123
|
-
- **Interactive publishing**: If not using the --publish-to-npm flag, the CLI will ask if you want to publish after generation
|
|
124
|
-
- **Registry configuration**: When choosing to publish interactively, you can specify a custom registry URL
|
|
125
|
-
- Checks if user is logged in before attempting to publish
|
|
126
|
-
- Temporarily sets and resets registry configuration when custom registry is used
|
|
127
|
-
|
|
128
|
-
Requirements for publishing:
|
|
129
|
-
- Must be logged in to npm (`npm login`)
|
|
130
|
-
- Package must have a valid package.json
|
|
131
|
-
- Package will be built automatically before publishing (if build script exists)
|
|
132
|
-
|
|
133
|
-
Publishing workflow:
|
|
134
|
-
1. If `--publish-to-npm` is not specified, CLI prompts after successful generation
|
|
135
|
-
2. If user chooses to publish, CLI asks for registry URL (optional)
|
|
136
|
-
3. Validates npm login status
|
|
137
|
-
4. Publishes to specified registry (no need to build separately)
|
|
138
|
-
|
|
139
|
-
### Output Directory Behavior
|
|
140
|
-
- Dart files are generated in the Flutter package source directory
|
|
141
|
-
- React/Vue files are generated in the specified output directory
|
|
142
|
-
- If no output directory is specified, a temporary directory is created
|
|
143
|
-
- Temporary directories are created in the system temp folder with prefix `webf-typings-`
|
|
144
|
-
- When using temporary directories, the path is displayed at the end of generation
|
|
145
|
-
- Paths can be absolute or relative to current working directory
|
|
146
|
-
|
|
147
|
-
### Generated Files Structure
|
|
148
|
-
When running `dartGen`:
|
|
149
|
-
- Dart binding files are generated with `_bindings_generated.dart` suffix
|
|
150
|
-
- Original `.d.ts` files are copied to the output directory maintaining their structure
|
|
151
|
-
- An `index.d.ts` file is generated with:
|
|
152
|
-
- TypeScript triple-slash references to all `.d.ts` files
|
|
153
|
-
- ES module exports for all type definitions
|
|
154
|
-
- Package metadata from `pubspec.yaml` in documentation comments
|
|
155
|
-
- Directory structure from source is preserved in the output
|
|
156
|
-
|
|
157
|
-
## Development Workflow
|
|
158
|
-
|
|
159
|
-
### Adding New Features
|
|
160
|
-
1. Update TypeScript interfaces/types
|
|
161
|
-
2. Implement feature with tests
|
|
162
|
-
3. Update templates if needed
|
|
163
|
-
4. Ensure all tests pass
|
|
164
|
-
5. Update this documentation
|
|
165
|
-
|
|
166
|
-
### Debugging
|
|
167
|
-
Use the logger for debugging:
|
|
168
|
-
```typescript
|
|
169
|
-
import { debug, info, warn, error } from './logger';
|
|
170
|
-
debug('Processing file:', filename);
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Template Modification
|
|
174
|
-
Templates are in `/templates/*.tpl`. When modifying:
|
|
175
|
-
1. Update the template file
|
|
176
|
-
2. Update the corresponding generator function
|
|
177
|
-
3. Ensure generated code follows style guidelines
|
|
178
|
-
|
|
179
|
-
## Common Issues and Solutions
|
|
180
|
-
|
|
181
|
-
### Issue: Boolean attributes treated as strings
|
|
182
|
-
**Solution**: Use `generateAttributeSetter` which handles type conversion
|
|
183
|
-
|
|
184
|
-
### Issue: Null type handling
|
|
185
|
-
**Solution**: Check for literal types containing null:
|
|
186
|
-
```typescript
|
|
187
|
-
if (type.kind === ts.SyntaxKind.LiteralType) {
|
|
188
|
-
const literalType = type as ts.LiteralTypeNode;
|
|
189
|
-
if (literalType.literal.kind === ts.SyntaxKind.NullKeyword) {
|
|
190
|
-
return FunctionArgumentType.null;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Issue: File changes not detected
|
|
196
|
-
**Solution**: Clear caches before generation:
|
|
197
|
-
```typescript
|
|
198
|
-
clearCaches();
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## Code Style
|
|
202
|
-
- Use async/await for asynchronous operations
|
|
203
|
-
- Implement proper error handling with try-catch
|
|
204
|
-
- Add descriptive error messages
|
|
205
|
-
- Use TypeScript strict mode
|
|
206
|
-
- Follow existing patterns in the codebase
|
package/README-zhCN.md
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
# WebF CLI
|
|
2
|
-
|
|
3
|
-
一个用于生成 Flutter 类和 Vue/React 组件声明文件的命令行工具。
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @openwebf/webf
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 使用方法
|
|
12
|
-
|
|
13
|
-
WebF CLI 提供三个主要命令:
|
|
14
|
-
|
|
15
|
-
### 1. 初始化类型定义
|
|
16
|
-
|
|
17
|
-
在项目中初始化 WebF 类型定义:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
webf init <目标目录>
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
这将在指定目录中创建必要的类型定义文件。
|
|
24
|
-
|
|
25
|
-
### 2. 创建新项目
|
|
26
|
-
|
|
27
|
-
创建一个新的 Vue 或 React 项目:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
webf create <目标目录> --framework <框架> --package-name <包名>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
选项:
|
|
34
|
-
- `--framework`:选择 'vue' 或 'react'
|
|
35
|
-
- `--package-name`:指定包名
|
|
36
|
-
|
|
37
|
-
示例:
|
|
38
|
-
```bash
|
|
39
|
-
webf create my-webf-app --framework react --package-name @myorg/my-webf-app
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 3. 生成代码
|
|
43
|
-
|
|
44
|
-
生成 Flutter 类和组件声明文件:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
webf generate <目标路径> --flutter-package-src <Flutter源码路径> --framework <框架>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
选项:
|
|
51
|
-
- `--flutter-package-src`:Flutter 包源码路径
|
|
52
|
-
- `--framework`:选择 'vue' 或 'react'
|
|
53
|
-
|
|
54
|
-
示例:
|
|
55
|
-
```bash
|
|
56
|
-
webf generate ./src --flutter-package-src ./flutter_package --framework react
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## 实现细节
|
|
60
|
-
|
|
61
|
-
### 类型系统
|
|
62
|
-
|
|
63
|
-
CLI 使用复杂的类型系统来处理各种数据类型:
|
|
64
|
-
|
|
65
|
-
- 基本类型:
|
|
66
|
-
- `string`(DOM 字符串)
|
|
67
|
-
- `number`(整数/浮点数)
|
|
68
|
-
- `boolean`(布尔值)
|
|
69
|
-
- `any`(任意类型)
|
|
70
|
-
- `void`(空类型)
|
|
71
|
-
- `null`(空值)
|
|
72
|
-
- `undefined`(未定义)
|
|
73
|
-
|
|
74
|
-
- 复杂类型:
|
|
75
|
-
- 数组:`Type[]`
|
|
76
|
-
- 函数:`Function`
|
|
77
|
-
- Promise:`Promise<Type>`
|
|
78
|
-
- 自定义事件:`CustomEvent`
|
|
79
|
-
- 布局依赖类型:`DependentsOnLayout<Type>`
|
|
80
|
-
|
|
81
|
-
### 命名约定和文件结构
|
|
82
|
-
|
|
83
|
-
#### 接口命名模式
|
|
84
|
-
|
|
85
|
-
CLI 遵循特定的接口命名模式:
|
|
86
|
-
|
|
87
|
-
1. **组件接口**:
|
|
88
|
-
- 属性接口:`{组件名称}Properties`
|
|
89
|
-
- 事件接口:`{组件名称}Events`
|
|
90
|
-
- 示例:`ButtonProperties`、`ButtonEvents`
|
|
91
|
-
|
|
92
|
-
2. **生成的文件名**:
|
|
93
|
-
- React 组件:`{组件名称}.tsx`
|
|
94
|
-
- Vue 组件:`{组件名称}.vue`
|
|
95
|
-
- Flutter 类:`{组件名称}.dart`
|
|
96
|
-
- 类型定义:`{组件名称}.d.ts`
|
|
97
|
-
|
|
98
|
-
3. **名称转换**:
|
|
99
|
-
- 组件名称提取:
|
|
100
|
-
- 从 `{名称}Properties` → `{名称}`
|
|
101
|
-
- 从 `{名称}Events` → `{名称}`
|
|
102
|
-
- 示例:`ButtonProperties` → `Button`
|
|
103
|
-
|
|
104
|
-
#### 生成的组件名称
|
|
105
|
-
|
|
106
|
-
1. **React 组件**:
|
|
107
|
-
- 标签名:`<{组件名称} />`
|
|
108
|
-
- 文件名:`{组件名称}.tsx`
|
|
109
|
-
- 示例:`ButtonProperties` → `<Button />` 在 `button.tsx` 中
|
|
110
|
-
|
|
111
|
-
2. **Vue 组件**:
|
|
112
|
-
- 标签名:`<{组件名称}-component />`
|
|
113
|
-
- 文件名:`{组件名称}.vue`
|
|
114
|
-
- 示例:`ButtonProperties` → `<button-component />` 在 `button.vue` 中
|
|
115
|
-
|
|
116
|
-
3. **Flutter 类**:
|
|
117
|
-
- 类名:`{组件名称}`
|
|
118
|
-
- 文件名:`{组件名称}.dart`
|
|
119
|
-
- 示例:`ButtonProperties` → `Button` 类在 `button.dart` 中
|
|
120
|
-
|
|
121
|
-
#### 类型定义文件
|
|
122
|
-
|
|
123
|
-
1. **文件位置**:
|
|
124
|
-
- React:`src/components/{组件名称}.d.ts`
|
|
125
|
-
- Vue:`src/components/{组件名称}.d.ts`
|
|
126
|
-
- Flutter:`lib/src/{组件名称}.dart`
|
|
127
|
-
|
|
128
|
-
2. **接口结构**:
|
|
129
|
-
```typescript
|
|
130
|
-
interface {组件名称}Properties {
|
|
131
|
-
// 属性
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
interface {组件名称}Events {
|
|
135
|
-
// 事件
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
3. **组件注册**:
|
|
140
|
-
- React:在 `index.ts` 中导出
|
|
141
|
-
- Vue:在组件声明文件中注册
|
|
142
|
-
- Flutter:在库文件中导出
|
|
143
|
-
|
|
144
|
-
### 组件生成
|
|
145
|
-
|
|
146
|
-
#### React 组件
|
|
147
|
-
- 生成带有适当类型定义的 TypeScript React 组件
|
|
148
|
-
- 处理带有正确事件类型的事件绑定
|
|
149
|
-
- 支持带有基于 Promise 返回类型的异步方法
|
|
150
|
-
- 将事件名称转换为 React 约定(如 `onClick`、`onChange`)
|
|
151
|
-
|
|
152
|
-
#### Vue 组件
|
|
153
|
-
- 生成 Vue 组件类型声明
|
|
154
|
-
- 支持 Vue 的事件系统
|
|
155
|
-
- 使用适当的 TypeScript 类型处理属性和事件
|
|
156
|
-
- 生成组件注册代码
|
|
157
|
-
|
|
158
|
-
#### Flutter 类
|
|
159
|
-
- 生成带有适当类型映射的 Dart 类
|
|
160
|
-
- 使用正确的参数类型处理方法声明
|
|
161
|
-
- 支持异步操作
|
|
162
|
-
- 生成适当的事件处理器类型
|
|
163
|
-
|
|
164
|
-
### 类型分析
|
|
165
|
-
|
|
166
|
-
CLI 使用 TypeScript 的编译器 API 来分析和处理类型定义:
|
|
167
|
-
|
|
168
|
-
1. 解析 TypeScript 接口声明
|
|
169
|
-
2. 分析类关系和继承
|
|
170
|
-
3. 处理方法签名和参数类型
|
|
171
|
-
4. 处理联合类型和复杂类型表达式
|
|
172
|
-
5. 为每个目标平台生成适当的类型映射
|
|
173
|
-
|
|
174
|
-
### 代码生成约定
|
|
175
|
-
|
|
176
|
-
1. **命名约定**:
|
|
177
|
-
- 属性:camelCase
|
|
178
|
-
- 事件:带 'on' 前缀的 camelCase
|
|
179
|
-
- 方法:camelCase
|
|
180
|
-
- 类:PascalCase
|
|
181
|
-
|
|
182
|
-
2. **类型映射**:
|
|
183
|
-
- TypeScript → Dart:
|
|
184
|
-
- `string` → `String`
|
|
185
|
-
- `number` → `int`/`double`
|
|
186
|
-
- `boolean` → `bool`
|
|
187
|
-
- `any` → `dynamic`
|
|
188
|
-
- `void` → `void`
|
|
189
|
-
|
|
190
|
-
- TypeScript → React/Vue:
|
|
191
|
-
- `string` → `string`
|
|
192
|
-
- `number` → `number`
|
|
193
|
-
- `boolean` → `boolean`
|
|
194
|
-
- `any` → `any`
|
|
195
|
-
- `void` → `void`
|
|
196
|
-
|
|
197
|
-
3. **事件处理**:
|
|
198
|
-
- React:`EventHandler<SyntheticEvent<Element>>`
|
|
199
|
-
- Vue:`Event`/`CustomEvent`
|
|
200
|
-
- Flutter:`EventHandler<Event>`
|
|
201
|
-
|
|
202
|
-
## 项目结构
|
|
203
|
-
|
|
204
|
-
运行命令后,您的项目将具有以下结构:
|
|
205
|
-
|
|
206
|
-
### React 项目
|
|
207
|
-
```
|
|
208
|
-
my-webf-app/
|
|
209
|
-
├── src/
|
|
210
|
-
│ ├── components/
|
|
211
|
-
│ │ ├── button.tsx
|
|
212
|
-
│ │ ├── button.d.ts
|
|
213
|
-
│ │ └── index.ts
|
|
214
|
-
│ ├── utils/
|
|
215
|
-
│ │ └── createComponent.ts
|
|
216
|
-
│ └── index.ts
|
|
217
|
-
├── package.json
|
|
218
|
-
├── tsconfig.json
|
|
219
|
-
└── tsup.config.ts
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Vue 项目
|
|
223
|
-
```
|
|
224
|
-
my-webf-app/
|
|
225
|
-
├── src/
|
|
226
|
-
│ ├── components/
|
|
227
|
-
│ │ ├── button.vue
|
|
228
|
-
│ │ └── button.d.ts
|
|
229
|
-
├── package.json
|
|
230
|
-
└── tsconfig.json
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## 依赖项
|
|
234
|
-
|
|
235
|
-
CLI 会自动为所选框架安装必要的依赖项:
|
|
236
|
-
- React:React 及相关类型定义
|
|
237
|
-
- Vue:Vue 及相关类型定义
|
|
238
|
-
|
|
239
|
-
## 开发
|
|
240
|
-
|
|
241
|
-
### 从源码构建
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
npm install
|
|
245
|
-
npm run build
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 测试
|
|
249
|
-
|
|
250
|
-
```bash
|
|
251
|
-
npm test
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## 许可证
|
|
255
|
-
|
|
256
|
-
ISC
|