@openwebf/webf 0.24.0 → 0.24.2
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/README.md +9 -1
- package/bin/webf.js +9 -1
- package/dist/agents.js +245 -0
- package/dist/analyzer.js +183 -121
- package/dist/commands.js +20 -9
- package/dist/generator.js +39 -16
- package/package.json +2 -1
- package/src/agents.ts +267 -0
- package/src/analyzer.ts +186 -114
- package/src/commands.ts +22 -12
- package/src/generator.ts +32 -12
- package/templates/module.package.json.tpl +17 -6
- package/templates/{module.tsup.config.ts.tpl → module.tsdown.config.ts.tpl} +2 -7
- package/templates/react.component.tsx.tpl +18 -2
- package/templates/react.package.json.tpl +16 -4
- package/templates/{react.tsup.config.ts.tpl → react.tsdown.config.ts.tpl} +2 -4
- package/test/agents-init.test.ts +80 -0
- package/test/analyzer.test.ts +45 -1
- package/test/commands.test.ts +4 -4
- package/test/generator.test.ts +37 -0
- package/test/react.test.ts +5 -0
package/README.md
CHANGED
|
@@ -10,6 +10,14 @@ npm install -g @openwebf/webf
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
+
### Initialize Claude Code Skills
|
|
14
|
+
|
|
15
|
+
The `webf agents init` command injects WebF Claude Code skills (from `@openwebf/claude-code-skills`) into your project and updates `CLAUDE.md` to reference them.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
webf agents init [project-dir]
|
|
19
|
+
```
|
|
20
|
+
|
|
13
21
|
### Generate Code
|
|
14
22
|
|
|
15
23
|
The `webf codegen` command generates Dart abstract classes and React/Vue components from TypeScript definitions. It automatically creates a project if needed.
|
|
@@ -142,7 +150,7 @@ my-webf-app/
|
|
|
142
150
|
│ └── index.ts
|
|
143
151
|
├── package.json
|
|
144
152
|
├── tsconfig.json
|
|
145
|
-
├──
|
|
153
|
+
├── tsdown.config.ts
|
|
146
154
|
├── global.d.ts
|
|
147
155
|
└── .gitignore
|
|
148
156
|
```
|
package/bin/webf.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { Command } = require('commander');
|
|
4
4
|
const version = require('../package.json').version;
|
|
5
|
-
const { generateCommand, generateModuleCommand } = require('../dist/commands');
|
|
5
|
+
const { generateCommand, generateModuleCommand, agentsInitCommand } = require('../dist/commands');
|
|
6
6
|
|
|
7
7
|
const program = new Command();
|
|
8
8
|
|
|
@@ -35,4 +35,12 @@ program
|
|
|
35
35
|
.description('Generate NPM package and Dart bindings for a WebF module from TypeScript interfaces (*.module.d.ts)')
|
|
36
36
|
.action(generateModuleCommand);
|
|
37
37
|
|
|
38
|
+
const agents = program.command('agents').description('Manage Claude Code agent configs');
|
|
39
|
+
|
|
40
|
+
agents
|
|
41
|
+
.command('init')
|
|
42
|
+
.argument('[projectDir]', 'Target project directory', '.')
|
|
43
|
+
.description('Initialize Claude Code skills config for a project')
|
|
44
|
+
.action(agentsInitCommand);
|
|
45
|
+
|
|
38
46
|
program.parse();
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.agentsInitCommand = agentsInitCommand;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const os_1 = __importDefault(require("os"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
20
|
+
const WEBF_AGENTS_BLOCK_START = '<!-- webf-agents:init start -->';
|
|
21
|
+
const WEBF_AGENTS_BLOCK_END = '<!-- webf-agents:init end -->';
|
|
22
|
+
function ensureDirSync(dirPath) {
|
|
23
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
function readFileIfExists(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
return fs_1.default.readFileSync(filePath);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === 'ENOENT')
|
|
31
|
+
return null;
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function backupFileSync(filePath) {
|
|
36
|
+
const timestamp = new Date()
|
|
37
|
+
.toISOString()
|
|
38
|
+
.replace(/[:.]/g, '')
|
|
39
|
+
.replace('T', '')
|
|
40
|
+
.replace('Z', 'Z');
|
|
41
|
+
const backupPath = `${filePath}.bak.${timestamp}`;
|
|
42
|
+
fs_1.default.copyFileSync(filePath, backupPath);
|
|
43
|
+
return backupPath;
|
|
44
|
+
}
|
|
45
|
+
function copyFileWithBackupSync(srcPath, destPath) {
|
|
46
|
+
const src = fs_1.default.readFileSync(srcPath);
|
|
47
|
+
const dest = readFileIfExists(destPath);
|
|
48
|
+
if (dest && Buffer.compare(src, dest) === 0)
|
|
49
|
+
return { changed: false, backupPath: null };
|
|
50
|
+
ensureDirSync(path_1.default.dirname(destPath));
|
|
51
|
+
let backupPath = null;
|
|
52
|
+
if (dest) {
|
|
53
|
+
backupPath = backupFileSync(destPath);
|
|
54
|
+
}
|
|
55
|
+
fs_1.default.writeFileSync(destPath, src);
|
|
56
|
+
return { changed: true, backupPath };
|
|
57
|
+
}
|
|
58
|
+
function copyDirRecursiveSync(srcDir, destDir, stats) {
|
|
59
|
+
ensureDirSync(destDir);
|
|
60
|
+
const entries = fs_1.default.readdirSync(srcDir, { withFileTypes: true });
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const srcPath = path_1.default.join(srcDir, entry.name);
|
|
63
|
+
const destPath = path_1.default.join(destDir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
copyDirRecursiveSync(srcPath, destPath, stats);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (entry.isFile()) {
|
|
69
|
+
const { changed, backupPath } = copyFileWithBackupSync(srcPath, destPath);
|
|
70
|
+
if (changed)
|
|
71
|
+
stats.filesWritten += 1;
|
|
72
|
+
else
|
|
73
|
+
stats.filesUnchanged += 1;
|
|
74
|
+
if (backupPath)
|
|
75
|
+
stats.backupsCreated += 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function listFilesRecursiveSync(dirPath) {
|
|
80
|
+
const out = [];
|
|
81
|
+
const entries = fs_1.default.readdirSync(dirPath, { withFileTypes: true });
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
const entryPath = path_1.default.join(dirPath, entry.name);
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
out.push(...listFilesRecursiveSync(entryPath));
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (entry.isFile())
|
|
89
|
+
out.push(entryPath);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function toPosixRelativePath(fromDir, absolutePath) {
|
|
94
|
+
const rel = path_1.default.relative(fromDir, absolutePath);
|
|
95
|
+
return rel.split(path_1.default.sep).join('/');
|
|
96
|
+
}
|
|
97
|
+
function parseSkillFrontmatter(skillMd) {
|
|
98
|
+
var _a;
|
|
99
|
+
const trimmed = skillMd.trimStart();
|
|
100
|
+
if (!trimmed.startsWith('---'))
|
|
101
|
+
return {};
|
|
102
|
+
const endIndex = trimmed.indexOf('\n---', 3);
|
|
103
|
+
if (endIndex === -1)
|
|
104
|
+
return {};
|
|
105
|
+
const fm = trimmed.slice(3, endIndex).trim();
|
|
106
|
+
try {
|
|
107
|
+
const parsed = (_a = yaml_1.default.parse(fm)) !== null && _a !== void 0 ? _a : {};
|
|
108
|
+
return { name: parsed === null || parsed === void 0 ? void 0 : parsed.name, description: parsed === null || parsed === void 0 ? void 0 : parsed.description };
|
|
109
|
+
}
|
|
110
|
+
catch (_b) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function updateOrAppendMarkedBlock(existing, newBlock) {
|
|
115
|
+
const startIndex = existing.indexOf(WEBF_AGENTS_BLOCK_START);
|
|
116
|
+
const endIndex = existing.indexOf(WEBF_AGENTS_BLOCK_END);
|
|
117
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
118
|
+
const before = existing.slice(0, startIndex).trimEnd();
|
|
119
|
+
const after = existing.slice(endIndex + WEBF_AGENTS_BLOCK_END.length).trimStart();
|
|
120
|
+
const next = [before, newBlock.trim(), after].filter(Boolean).join('\n\n');
|
|
121
|
+
return { content: next + '\n', action: 'replaced' };
|
|
122
|
+
}
|
|
123
|
+
const content = existing.trimEnd();
|
|
124
|
+
return { content: (content ? content + '\n\n' : '') + newBlock.trim() + '\n', action: 'appended' };
|
|
125
|
+
}
|
|
126
|
+
function buildClaudeBlock(sourcePackageName, sourcePackageVersion, skills) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
lines.push(WEBF_AGENTS_BLOCK_START);
|
|
129
|
+
lines.push('## WebF Claude Code Skills');
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push(`Source: \`${sourcePackageName}@${sourcePackageVersion}\``);
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push('### Skills');
|
|
134
|
+
for (const skill of skills) {
|
|
135
|
+
const desc = skill.description ? ` — ${skill.description}` : '';
|
|
136
|
+
lines.push(`- \`${skill.name}\`${desc} (\`${skill.skillFileRelativePath}\`)`);
|
|
137
|
+
}
|
|
138
|
+
const anyReferences = skills.some(s => s.referenceRelativePaths.length > 0);
|
|
139
|
+
if (anyReferences) {
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push('### References');
|
|
142
|
+
for (const skill of skills) {
|
|
143
|
+
if (skill.referenceRelativePaths.length === 0)
|
|
144
|
+
continue;
|
|
145
|
+
const refs = skill.referenceRelativePaths.map(r => `\`${r}\``).join(', ');
|
|
146
|
+
lines.push(`- \`${skill.name}\`: ${refs}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
lines.push(WEBF_AGENTS_BLOCK_END);
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
152
|
+
function resolveSkillsPackageRoot() {
|
|
153
|
+
var _a, _b;
|
|
154
|
+
const packageJsonPath = require.resolve('@openwebf/claude-code-skills/package.json');
|
|
155
|
+
const packageRoot = path_1.default.dirname(packageJsonPath);
|
|
156
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
157
|
+
return {
|
|
158
|
+
packageRoot,
|
|
159
|
+
packageName: (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.name) !== null && _a !== void 0 ? _a : '@openwebf/claude-code-skills',
|
|
160
|
+
packageVersion: (_b = packageJson === null || packageJson === void 0 ? void 0 : packageJson.version) !== null && _b !== void 0 ? _b : 'unknown',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function listSkillDirectories(skillsPackageRoot) {
|
|
164
|
+
const entries = fs_1.default.readdirSync(skillsPackageRoot, { withFileTypes: true });
|
|
165
|
+
const dirs = entries
|
|
166
|
+
.filter(e => e.isDirectory())
|
|
167
|
+
.map(e => e.name)
|
|
168
|
+
.filter(name => fs_1.default.existsSync(path_1.default.join(skillsPackageRoot, name, 'SKILL.md')))
|
|
169
|
+
.sort();
|
|
170
|
+
return dirs;
|
|
171
|
+
}
|
|
172
|
+
function collectSkillInfo(projectRoot, skillsDir, skillDirectoryName) {
|
|
173
|
+
var _a;
|
|
174
|
+
const skillDirAbs = path_1.default.join(skillsDir, skillDirectoryName);
|
|
175
|
+
const skillMdAbs = path_1.default.join(skillDirAbs, 'SKILL.md');
|
|
176
|
+
const skillMd = fs_1.default.readFileSync(skillMdAbs, 'utf-8');
|
|
177
|
+
const fm = parseSkillFrontmatter(skillMd);
|
|
178
|
+
const referenceRelativePaths = [];
|
|
179
|
+
const files = listFilesRecursiveSync(skillDirAbs);
|
|
180
|
+
for (const fileAbs of files) {
|
|
181
|
+
if (path_1.default.basename(fileAbs) === 'SKILL.md')
|
|
182
|
+
continue;
|
|
183
|
+
referenceRelativePaths.push(toPosixRelativePath(projectRoot, fileAbs));
|
|
184
|
+
}
|
|
185
|
+
referenceRelativePaths.sort();
|
|
186
|
+
return {
|
|
187
|
+
directoryName: skillDirectoryName,
|
|
188
|
+
name: (_a = fm.name) !== null && _a !== void 0 ? _a : skillDirectoryName,
|
|
189
|
+
description: fm.description,
|
|
190
|
+
skillFileRelativePath: toPosixRelativePath(projectRoot, skillMdAbs),
|
|
191
|
+
referenceRelativePaths,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function agentsInitCommand(projectDir) {
|
|
195
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
var _a, _b;
|
|
197
|
+
const startedAt = Date.now();
|
|
198
|
+
const resolvedProjectDir = path_1.default.resolve(process.cwd(), projectDir || '.');
|
|
199
|
+
const claudeMdPath = path_1.default.join(resolvedProjectDir, 'CLAUDE.md');
|
|
200
|
+
const claudeDir = path_1.default.join(resolvedProjectDir, '.claude');
|
|
201
|
+
const projectSkillsDir = path_1.default.join(claudeDir, 'skills');
|
|
202
|
+
const hasClaudeMd = fs_1.default.existsSync(claudeMdPath);
|
|
203
|
+
const hasClaudeDir = fs_1.default.existsSync(claudeDir);
|
|
204
|
+
const isNewProject = !hasClaudeMd && !hasClaudeDir;
|
|
205
|
+
console.log('webf agents init');
|
|
206
|
+
console.log(`Project: ${resolvedProjectDir}`);
|
|
207
|
+
if (isNewProject) {
|
|
208
|
+
console.log('Detected: no CLAUDE.md and no .claude/ (new project)');
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.log(`Detected: CLAUDE.md=${hasClaudeMd ? 'yes' : 'no'}, .claude/=${hasClaudeDir ? 'yes' : 'no'} (existing project)`);
|
|
212
|
+
}
|
|
213
|
+
const { packageRoot, packageName, packageVersion } = resolveSkillsPackageRoot();
|
|
214
|
+
const skillDirectories = listSkillDirectories(packageRoot);
|
|
215
|
+
if (skillDirectories.length === 0) {
|
|
216
|
+
throw new Error(`No skills found in ${packageName} (resolved at ${packageRoot}).`);
|
|
217
|
+
}
|
|
218
|
+
console.log(`Skills source: ${packageName}@${packageVersion}`);
|
|
219
|
+
console.log(`Skills destination: ${toPosixRelativePath(resolvedProjectDir, projectSkillsDir)}`);
|
|
220
|
+
ensureDirSync(projectSkillsDir);
|
|
221
|
+
const copyStats = { filesWritten: 0, filesUnchanged: 0, backupsCreated: 0 };
|
|
222
|
+
for (const skillDirName of skillDirectories) {
|
|
223
|
+
const srcSkillDir = path_1.default.join(packageRoot, skillDirName);
|
|
224
|
+
const destSkillDir = path_1.default.join(projectSkillsDir, skillDirName);
|
|
225
|
+
copyDirRecursiveSync(srcSkillDir, destSkillDir, copyStats);
|
|
226
|
+
}
|
|
227
|
+
const installedSkills = skillDirectories.map(skillDirName => collectSkillInfo(resolvedProjectDir, projectSkillsDir, skillDirName));
|
|
228
|
+
const block = buildClaudeBlock(packageName, packageVersion, installedSkills);
|
|
229
|
+
if (isNewProject) {
|
|
230
|
+
const initial = ['# Claude Code', '', block, ''].join('\n');
|
|
231
|
+
fs_1.default.writeFileSync(claudeMdPath, initial, 'utf-8');
|
|
232
|
+
console.log(`Created ${toPosixRelativePath(resolvedProjectDir, claudeMdPath)}`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const existing = (_b = (_a = readFileIfExists(claudeMdPath)) === null || _a === void 0 ? void 0 : _a.toString('utf-8')) !== null && _b !== void 0 ? _b : '';
|
|
236
|
+
const { content, action } = updateOrAppendMarkedBlock(existing, block);
|
|
237
|
+
fs_1.default.writeFileSync(claudeMdPath, content, 'utf-8');
|
|
238
|
+
console.log(`${action === 'replaced' ? 'Updated' : 'Appended'} WebF skills block in ${toPosixRelativePath(resolvedProjectDir, claudeMdPath)}`);
|
|
239
|
+
}
|
|
240
|
+
const versionFilePath = path_1.default.join(claudeDir, 'webf-claude-code-skills.version');
|
|
241
|
+
fs_1.default.writeFileSync(versionFilePath, `${packageName}@${packageVersion}${os_1.default.EOL}`, 'utf-8');
|
|
242
|
+
console.log(`Wrote ${toPosixRelativePath(resolvedProjectDir, versionFilePath)}`);
|
|
243
|
+
console.log(`Installed ${installedSkills.length} skills (${copyStats.filesWritten} files written, ${copyStats.filesUnchanged} unchanged, ${copyStats.backupsCreated} backups) in ${Date.now() - startedAt}ms`);
|
|
244
|
+
});
|
|
245
|
+
}
|
package/dist/analyzer.js
CHANGED
|
@@ -365,134 +365,196 @@ function handleGenericWrapper(typeReference, mode) {
|
|
|
365
365
|
const argument = typeReference.typeArguments[0];
|
|
366
366
|
return getParameterBaseType(argument, mode);
|
|
367
367
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (
|
|
372
|
-
return
|
|
368
|
+
const customEventTypePrinter = typescript_1.default.createPrinter({ removeComments: true });
|
|
369
|
+
function mapTypeReferenceIdentifierToTsType(identifier) {
|
|
370
|
+
const mappedType = TYPE_REFERENCE_MAP[identifier];
|
|
371
|
+
if (mappedType === undefined)
|
|
372
|
+
return null;
|
|
373
|
+
switch (mappedType) {
|
|
374
|
+
case declaration_1.FunctionArgumentType.boolean:
|
|
375
|
+
return 'boolean';
|
|
376
|
+
case declaration_1.FunctionArgumentType.dom_string:
|
|
377
|
+
return 'string';
|
|
378
|
+
case declaration_1.FunctionArgumentType.double:
|
|
379
|
+
case declaration_1.FunctionArgumentType.int:
|
|
380
|
+
return 'number';
|
|
381
|
+
case declaration_1.FunctionArgumentType.any:
|
|
382
|
+
return 'any';
|
|
383
|
+
case declaration_1.FunctionArgumentType.void:
|
|
384
|
+
return 'void';
|
|
385
|
+
case declaration_1.FunctionArgumentType.function:
|
|
386
|
+
return 'Function';
|
|
387
|
+
case declaration_1.FunctionArgumentType.promise:
|
|
388
|
+
return 'Promise<any>';
|
|
389
|
+
default:
|
|
390
|
+
return null;
|
|
373
391
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return JSON.stringify(lit.text);
|
|
389
|
-
return 'any';
|
|
390
|
-
}
|
|
391
|
-
// Basic keywords: boolean, string, number, null, undefined
|
|
392
|
-
const basic = BASIC_TYPE_MAP[t.kind];
|
|
393
|
-
if (basic !== undefined) {
|
|
394
|
-
switch (basic) {
|
|
395
|
-
case declaration_1.FunctionArgumentType.boolean:
|
|
396
|
-
return 'boolean';
|
|
397
|
-
case declaration_1.FunctionArgumentType.dom_string:
|
|
398
|
-
return 'string';
|
|
399
|
-
case declaration_1.FunctionArgumentType.double:
|
|
400
|
-
case declaration_1.FunctionArgumentType.int:
|
|
401
|
-
return 'number';
|
|
402
|
-
case declaration_1.FunctionArgumentType.null:
|
|
403
|
-
return 'null';
|
|
404
|
-
case declaration_1.FunctionArgumentType.undefined:
|
|
405
|
-
return 'undefined';
|
|
406
|
-
default:
|
|
407
|
-
return 'any';
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// Literal null/undefined keywords that BASIC_TYPE_MAP may not cover
|
|
411
|
-
if (t.kind === typescript_1.default.SyntaxKind.NullKeyword)
|
|
412
|
-
return 'null';
|
|
413
|
-
if (t.kind === typescript_1.default.SyntaxKind.UndefinedKeyword)
|
|
414
|
-
return 'undefined';
|
|
415
|
-
// Fallback: rely on toString of node kind
|
|
392
|
+
}
|
|
393
|
+
function getBasicTypeKindAsTsType(kind) {
|
|
394
|
+
const basicType = BASIC_TYPE_MAP[kind];
|
|
395
|
+
if (basicType === undefined)
|
|
396
|
+
return null;
|
|
397
|
+
switch (basicType) {
|
|
398
|
+
case declaration_1.FunctionArgumentType.boolean:
|
|
399
|
+
return 'boolean';
|
|
400
|
+
case declaration_1.FunctionArgumentType.dom_string:
|
|
401
|
+
return 'string';
|
|
402
|
+
case declaration_1.FunctionArgumentType.double:
|
|
403
|
+
case declaration_1.FunctionArgumentType.int:
|
|
404
|
+
return 'number';
|
|
405
|
+
case declaration_1.FunctionArgumentType.any:
|
|
416
406
|
return 'any';
|
|
417
|
-
|
|
418
|
-
|
|
407
|
+
case declaration_1.FunctionArgumentType.void:
|
|
408
|
+
return 'void';
|
|
409
|
+
case declaration_1.FunctionArgumentType.null:
|
|
410
|
+
return 'null';
|
|
411
|
+
case declaration_1.FunctionArgumentType.undefined:
|
|
412
|
+
return 'undefined';
|
|
413
|
+
default:
|
|
414
|
+
return null;
|
|
419
415
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
416
|
+
}
|
|
417
|
+
function stringifyEntityName(name) {
|
|
418
|
+
if (typescript_1.default.isIdentifier(name))
|
|
419
|
+
return name.text;
|
|
420
|
+
return `${stringifyEntityName(name.left)}.${name.right.text}`;
|
|
421
|
+
}
|
|
422
|
+
function safePrintCustomEventNode(node) {
|
|
423
|
+
const sourceFile = node.getSourceFile();
|
|
424
|
+
const printed = customEventTypePrinter.printNode(typescript_1.default.EmitHint.Unspecified, node, sourceFile);
|
|
425
|
+
// Ensure WebF IDL-like aliases used in type definitions do not leak into generated TypeScript packages.
|
|
426
|
+
return printed.replace(/\bint\b/g, 'number').replace(/\bdouble\b/g, 'number');
|
|
427
|
+
}
|
|
428
|
+
function stringifyCustomEventGenericTypeNode(typeNode) {
|
|
429
|
+
if (typescript_1.default.isParenthesizedTypeNode(typeNode)) {
|
|
430
|
+
const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
|
|
431
|
+
return inner ? `(${inner})` : null;
|
|
432
|
+
}
|
|
433
|
+
if (typescript_1.default.isUnionTypeNode(typeNode)) {
|
|
434
|
+
const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t) => Boolean(t));
|
|
435
|
+
return parts.length === typeNode.types.length ? parts.join(' | ') : null;
|
|
436
|
+
}
|
|
437
|
+
if (typescript_1.default.isIntersectionTypeNode(typeNode)) {
|
|
438
|
+
const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t) => Boolean(t));
|
|
439
|
+
return parts.length === typeNode.types.length ? parts.join(' & ') : null;
|
|
440
|
+
}
|
|
441
|
+
if (typescript_1.default.isArrayTypeNode(typeNode)) {
|
|
442
|
+
const element = stringifyCustomEventGenericTypeNode(typeNode.elementType);
|
|
443
|
+
return element ? `${element}[]` : null;
|
|
444
|
+
}
|
|
445
|
+
if (typescript_1.default.isTupleTypeNode(typeNode)) {
|
|
446
|
+
const elements = typeNode.elements.map(e => stringifyCustomEventGenericTypeNode(e)).filter((t) => Boolean(t));
|
|
447
|
+
return elements.length === typeNode.elements.length ? `[${elements.join(', ')}]` : null;
|
|
448
|
+
}
|
|
449
|
+
if (typescript_1.default.isLiteralTypeNode(typeNode)) {
|
|
450
|
+
const literal = typeNode.literal;
|
|
451
|
+
if (literal.kind === typescript_1.default.SyntaxKind.NullKeyword)
|
|
452
|
+
return 'null';
|
|
453
|
+
if (literal.kind === typescript_1.default.SyntaxKind.UndefinedKeyword)
|
|
454
|
+
return 'undefined';
|
|
455
|
+
if (literal.kind === typescript_1.default.SyntaxKind.TrueKeyword)
|
|
456
|
+
return 'true';
|
|
457
|
+
if (literal.kind === typescript_1.default.SyntaxKind.FalseKeyword)
|
|
458
|
+
return 'false';
|
|
459
|
+
if (typescript_1.default.isStringLiteral(literal))
|
|
460
|
+
return JSON.stringify(literal.text);
|
|
461
|
+
if (typescript_1.default.isNumericLiteral(literal))
|
|
462
|
+
return literal.text;
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
const basic = getBasicTypeKindAsTsType(typeNode.kind);
|
|
466
|
+
if (basic)
|
|
467
|
+
return basic;
|
|
468
|
+
if (typescript_1.default.isTypeReferenceNode(typeNode)) {
|
|
469
|
+
const typeName = stringifyEntityName(typeNode.typeName);
|
|
470
|
+
// Unwrap internal helpers used by WebF typings.
|
|
471
|
+
if (typeName === 'DartImpl' && typeNode.typeArguments && typeNode.typeArguments[0]) {
|
|
472
|
+
return stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
|
|
473
|
+
}
|
|
474
|
+
if (typeName === 'Promise') {
|
|
475
|
+
if (!typeNode.typeArguments || !typeNode.typeArguments[0])
|
|
476
|
+
return 'Promise<any>';
|
|
477
|
+
const inner = stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
|
|
478
|
+
return inner ? `Promise<${inner}>` : null;
|
|
479
|
+
}
|
|
480
|
+
const mapped = mapTypeReferenceIdentifierToTsType(typeName);
|
|
481
|
+
if (mapped)
|
|
482
|
+
return mapped;
|
|
483
|
+
if (!typeNode.typeArguments || typeNode.typeArguments.length === 0) {
|
|
484
|
+
return typeName;
|
|
485
|
+
}
|
|
486
|
+
const args = typeNode.typeArguments
|
|
487
|
+
.map(arg => stringifyCustomEventGenericTypeNode(arg))
|
|
488
|
+
.filter((t) => Boolean(t));
|
|
489
|
+
if (args.length !== typeNode.typeArguments.length)
|
|
490
|
+
return null;
|
|
491
|
+
return `${typeName}<${args.join(', ')}>`;
|
|
492
|
+
}
|
|
493
|
+
if (typescript_1.default.isTypeLiteralNode(typeNode)) {
|
|
494
|
+
const members = [];
|
|
495
|
+
for (const member of typeNode.members) {
|
|
496
|
+
if (typescript_1.default.isPropertySignature(member) && member.type) {
|
|
497
|
+
const typeString = stringifyCustomEventGenericTypeNode(member.type);
|
|
498
|
+
if (!typeString)
|
|
499
|
+
return null;
|
|
500
|
+
let nameText;
|
|
501
|
+
if (typescript_1.default.isIdentifier(member.name))
|
|
502
|
+
nameText = member.name.text;
|
|
503
|
+
else if (typescript_1.default.isStringLiteral(member.name))
|
|
504
|
+
nameText = JSON.stringify(member.name.text);
|
|
505
|
+
else if (typescript_1.default.isNumericLiteral(member.name))
|
|
506
|
+
nameText = member.name.text;
|
|
507
|
+
else
|
|
508
|
+
nameText = member.name.getText();
|
|
509
|
+
const optional = member.questionToken ? '?' : '';
|
|
510
|
+
members.push(`${nameText}${optional}: ${typeString}`);
|
|
511
|
+
continue;
|
|
450
512
|
}
|
|
513
|
+
if (typescript_1.default.isIndexSignatureDeclaration(member) && member.type && member.parameters.length === 1) {
|
|
514
|
+
const param = member.parameters[0];
|
|
515
|
+
const paramName = typescript_1.default.isIdentifier(param.name) ? param.name.text : param.name.getText();
|
|
516
|
+
const paramType = param.type ? stringifyCustomEventGenericTypeNode(param.type) : 'string';
|
|
517
|
+
const valueType = stringifyCustomEventGenericTypeNode(member.type);
|
|
518
|
+
if (!paramType || !valueType)
|
|
519
|
+
return null;
|
|
520
|
+
members.push(`[${paramName}: ${paramType}]: ${valueType}`);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
// Fallback for uncommon members (call signatures, method signatures, etc.).
|
|
524
|
+
members.push(safePrintCustomEventNode(member));
|
|
451
525
|
}
|
|
452
|
-
|
|
453
|
-
// For other type references, use the type name directly
|
|
454
|
-
genericType = typeName;
|
|
455
|
-
}
|
|
526
|
+
return `{ ${members.join('; ')} }`;
|
|
456
527
|
}
|
|
457
|
-
|
|
458
|
-
|
|
528
|
+
if (typescript_1.default.isTypeOperatorNode(typeNode)) {
|
|
529
|
+
const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
|
|
530
|
+
if (!inner)
|
|
531
|
+
return null;
|
|
532
|
+
const operator = typeNode.operator === typescript_1.default.SyntaxKind.KeyOfKeyword ? 'keyof' :
|
|
533
|
+
typeNode.operator === typescript_1.default.SyntaxKind.ReadonlyKeyword ? 'readonly' :
|
|
534
|
+
typeNode.operator === typescript_1.default.SyntaxKind.UniqueKeyword ? 'unique' :
|
|
535
|
+
null;
|
|
536
|
+
return operator ? `${operator} ${inner}` : null;
|
|
537
|
+
}
|
|
538
|
+
if (typescript_1.default.isIndexedAccessTypeNode(typeNode)) {
|
|
539
|
+
const objectType = stringifyCustomEventGenericTypeNode(typeNode.objectType);
|
|
540
|
+
const indexType = stringifyCustomEventGenericTypeNode(typeNode.indexType);
|
|
541
|
+
if (!objectType || !indexType)
|
|
542
|
+
return null;
|
|
543
|
+
return `${objectType}[${indexType}]`;
|
|
459
544
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
genericType = 'number';
|
|
474
|
-
break;
|
|
475
|
-
case declaration_1.FunctionArgumentType.any:
|
|
476
|
-
genericType = 'any';
|
|
477
|
-
break;
|
|
478
|
-
case declaration_1.FunctionArgumentType.void:
|
|
479
|
-
genericType = 'void';
|
|
480
|
-
break;
|
|
481
|
-
case declaration_1.FunctionArgumentType.null:
|
|
482
|
-
genericType = 'null';
|
|
483
|
-
break;
|
|
484
|
-
case declaration_1.FunctionArgumentType.undefined:
|
|
485
|
-
genericType = 'undefined';
|
|
486
|
-
break;
|
|
487
|
-
default:
|
|
488
|
-
genericType = 'any';
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
// For truly complex types, fallback to 'any' to avoid errors
|
|
493
|
-
console.warn('Complex generic type in CustomEvent, using any');
|
|
494
|
-
genericType = 'any';
|
|
495
|
-
}
|
|
545
|
+
// As a last resort, keep the original syntax but normalize known WebF aliases.
|
|
546
|
+
return safePrintCustomEventNode(typeNode);
|
|
547
|
+
}
|
|
548
|
+
function handleCustomEventType(typeReference) {
|
|
549
|
+
// Handle CustomEvent<T> by returning the full type with generic parameter
|
|
550
|
+
if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
|
|
551
|
+
return 'CustomEvent';
|
|
552
|
+
}
|
|
553
|
+
const argument = typeReference.typeArguments[0];
|
|
554
|
+
const genericType = stringifyCustomEventGenericTypeNode(argument);
|
|
555
|
+
if (!genericType) {
|
|
556
|
+
console.warn('Complex generic type in CustomEvent, using any');
|
|
557
|
+
return 'CustomEvent<any>';
|
|
496
558
|
}
|
|
497
559
|
return `CustomEvent<${genericType}>`;
|
|
498
560
|
}
|