@openwebf/webf 0.23.10 → 0.24.1

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 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.
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/commands.js CHANGED
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.agentsInitCommand = void 0;
15
16
  exports.generateCommand = generateCommand;
16
17
  exports.generateModuleCommand = generateModuleCommand;
17
18
  const child_process_1 = require("child_process");
@@ -20,10 +21,13 @@ const path_1 = __importDefault(require("path"));
20
21
  const os_1 = __importDefault(require("os"));
21
22
  const generator_1 = require("./generator");
22
23
  const module_1 = require("./module");
24
+ const peerDeps_1 = require("./peerDeps");
23
25
  const glob_1 = require("glob");
24
26
  const lodash_1 = __importDefault(require("lodash"));
25
27
  const inquirer_1 = __importDefault(require("inquirer"));
26
28
  const yaml_1 = __importDefault(require("yaml"));
29
+ const agents_1 = require("./agents");
30
+ Object.defineProperty(exports, "agentsInitCommand", { enumerable: true, get: function () { return agents_1.agentsInitCommand; } });
27
31
  /**
28
32
  * Sanitize a package name to comply with npm naming rules
29
33
  * NPM package name rules:
@@ -350,7 +354,8 @@ function createCommand(target, options) {
350
354
  // Do not overwrite existing index.ts created by the user
351
355
  // Leave merge to the codegen step which appends exports safely
352
356
  }
353
- (0, child_process_1.spawnSync)(NPM, ['install'], {
357
+ // Ensure devDependencies are installed even if the user's shell has NODE_ENV=production.
358
+ (0, child_process_1.spawnSync)(NPM, ['install', '--production=false'], {
354
359
  cwd: target,
355
360
  stdio: 'inherit'
356
361
  });
@@ -369,11 +374,8 @@ function createCommand(target, options) {
369
374
  const gitignorePath = path_1.default.join(target, '.gitignore');
370
375
  const gitignoreContent = lodash_1.default.template(gitignore)({});
371
376
  writeFileIfChanged(gitignorePath, gitignoreContent);
372
- (0, child_process_1.spawnSync)(NPM, ['install', '@openwebf/webf-enterprise-typings'], {
373
- cwd: target,
374
- stdio: 'inherit'
375
- });
376
- (0, child_process_1.spawnSync)(NPM, ['install', 'vue', '-D'], {
377
+ // Ensure devDependencies are installed even if the user's shell has NODE_ENV=production.
378
+ (0, child_process_1.spawnSync)(NPM, ['install', '--production=false'], {
377
379
  cwd: target,
378
380
  stdio: 'inherit'
379
381
  });
@@ -1003,7 +1005,7 @@ function ensureInitialized(targetPath) {
1003
1005
  }
1004
1006
  function buildPackage(packagePath) {
1005
1007
  return __awaiter(this, void 0, void 0, function* () {
1006
- var _a;
1008
+ var _a, _b, _c;
1007
1009
  const packageJsonPath = path_1.default.join(packagePath, 'package.json');
1008
1010
  if (!fs_1.default.existsSync(packageJsonPath)) {
1009
1011
  // Skip the error in test environment to avoid console warnings
@@ -1015,6 +1017,91 @@ function buildPackage(packagePath) {
1015
1017
  const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
1016
1018
  const packageName = packageJson.name;
1017
1019
  const packageVersion = packageJson.version;
1020
+ function getInstalledPackageJsonPath(pkgName) {
1021
+ const parts = pkgName.split('/');
1022
+ return path_1.default.join(packagePath, 'node_modules', ...parts, 'package.json');
1023
+ }
1024
+ function getInstalledPackageDir(pkgName) {
1025
+ const parts = pkgName.split('/');
1026
+ return path_1.default.join(packagePath, 'node_modules', ...parts);
1027
+ }
1028
+ function findUp(startDir, relativePathToFind) {
1029
+ let dir = path_1.default.resolve(startDir);
1030
+ while (true) {
1031
+ const candidate = path_1.default.join(dir, relativePathToFind);
1032
+ if (fs_1.default.existsSync(candidate))
1033
+ return candidate;
1034
+ const parent = path_1.default.dirname(dir);
1035
+ if (parent === dir)
1036
+ return null;
1037
+ dir = parent;
1038
+ }
1039
+ }
1040
+ function ensurePeerDependencyAvailableForBuild(peerName) {
1041
+ var _a, _b;
1042
+ const installedPkgJson = getInstalledPackageJsonPath(peerName);
1043
+ if (fs_1.default.existsSync(installedPkgJson))
1044
+ return;
1045
+ const peerRange = (_a = packageJson.peerDependencies) === null || _a === void 0 ? void 0 : _a[peerName];
1046
+ const localMap = {
1047
+ '@openwebf/react-core-ui': path_1.default.join('packages', 'react-core-ui'),
1048
+ '@openwebf/vue-core-ui': path_1.default.join('packages', 'vue-core-ui'),
1049
+ };
1050
+ let installSpec = null;
1051
+ const localRel = localMap[peerName];
1052
+ if (localRel) {
1053
+ const localPath = findUp(process.cwd(), localRel);
1054
+ if (localPath) {
1055
+ if (!(0, peerDeps_1.isPackageTypesReady)(localPath)) {
1056
+ const localPkgJsonPath = path_1.default.join(localPath, 'package.json');
1057
+ if (fs_1.default.existsSync(localPkgJsonPath)) {
1058
+ const localPkgJson = (0, peerDeps_1.readJsonFile)(localPkgJsonPath);
1059
+ if ((_b = localPkgJson.scripts) === null || _b === void 0 ? void 0 : _b.build) {
1060
+ if (process.env.WEBF_CODEGEN_BUILD_LOCAL_PEERS !== '1') {
1061
+ console.warn(`\n⚠️ Local ${peerName} found at ${localPath} but type declarations are missing; falling back to registry install.`);
1062
+ }
1063
+ else {
1064
+ console.log(`\n🔧 Local ${peerName} found at ${localPath} but build artifacts are missing; building it for DTS...`);
1065
+ const buildLocalResult = (0, child_process_1.spawnSync)(NPM, ['run', 'build'], {
1066
+ cwd: localPath,
1067
+ stdio: 'inherit'
1068
+ });
1069
+ if (buildLocalResult.status === 0) {
1070
+ if ((0, peerDeps_1.isPackageTypesReady)(localPath)) {
1071
+ installSpec = localPath;
1072
+ }
1073
+ else {
1074
+ console.warn(`\n⚠️ Built local ${peerName} but type declarations are still missing; falling back to registry install.`);
1075
+ }
1076
+ }
1077
+ else {
1078
+ console.warn(`\n⚠️ Failed to build local ${peerName}; falling back to registry install.`);
1079
+ }
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+ else {
1085
+ installSpec = localPath;
1086
+ }
1087
+ }
1088
+ }
1089
+ if (!installSpec) {
1090
+ installSpec = peerRange ? `${peerName}@${peerRange}` : peerName;
1091
+ }
1092
+ console.log(`\n📦 Installing peer dependency for build: ${peerName}...`);
1093
+ const installResult = (0, child_process_1.spawnSync)(NPM, ['install', '--no-save', installSpec], {
1094
+ cwd: packagePath,
1095
+ stdio: 'inherit'
1096
+ });
1097
+ if (installResult.status !== 0) {
1098
+ throw new Error(`Failed to install peer dependency for build: ${peerName}`);
1099
+ }
1100
+ const installedTypesFile = (0, peerDeps_1.getPackageTypesFileFromDir)(getInstalledPackageDir(peerName));
1101
+ if (installedTypesFile && !fs_1.default.existsSync(installedTypesFile)) {
1102
+ throw new Error(`Peer dependency ${peerName} was installed but type declarations were not found at ${installedTypesFile}`);
1103
+ }
1104
+ }
1018
1105
  // Check if node_modules exists
1019
1106
  const nodeModulesPath = path_1.default.join(packagePath, 'node_modules');
1020
1107
  if (!fs_1.default.existsSync(nodeModulesPath)) {
@@ -1035,6 +1122,13 @@ function buildPackage(packagePath) {
1035
1122
  }
1036
1123
  // Check if package has a build script
1037
1124
  if ((_a = packageJson.scripts) === null || _a === void 0 ? void 0 : _a.build) {
1125
+ // DTS build needs peer deps present locally to resolve types (even though they are not bundled).
1126
+ if ((_b = packageJson.peerDependencies) === null || _b === void 0 ? void 0 : _b['@openwebf/react-core-ui']) {
1127
+ ensurePeerDependencyAvailableForBuild('@openwebf/react-core-ui');
1128
+ }
1129
+ if ((_c = packageJson.peerDependencies) === null || _c === void 0 ? void 0 : _c['@openwebf/vue-core-ui']) {
1130
+ ensurePeerDependencyAvailableForBuild('@openwebf/vue-core-ui');
1131
+ }
1038
1132
  console.log(`\nBuilding ${packageName}@${packageVersion}...`);
1039
1133
  const buildResult = (0, child_process_1.spawnSync)(NPM, ['run', 'build'], {
1040
1134
  cwd: packagePath,
package/dist/generator.js CHANGED
@@ -378,11 +378,8 @@ function reactGen(_a) {
378
378
  (0, logger_1.warn)(`Failed to merge into existing index.ts. Skipping modifications: ${indexFilePath}`);
379
379
  }
380
380
  }
381
- (0, logger_1.timeEnd)('reactGen');
382
- (0, logger_1.success)(`React code generation completed. ${filesChanged} files changed.`);
383
- (0, logger_1.info)(`Output directory: ${normalizedTarget}`);
384
- (0, logger_1.info)('You can now import these components in your React project.');
385
- // Aggregate standalone type declarations (consts/enums/type aliases) into a single types.ts
381
+ // Always generate src/types.ts so generated components can safely import it.
382
+ // When there are no standalone declarations, emit an empty module (`export {};`).
386
383
  try {
387
384
  const consts = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.ConstObject));
388
385
  const enums = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.EnumObject));
@@ -395,38 +392,38 @@ function reactGen(_a) {
395
392
  typeAliases.forEach(t => { if (!typeAliasMap.has(t.name))
396
393
  typeAliasMap.set(t.name, t); });
397
394
  const hasAny = constMap.size > 0 || enums.length > 0 || typeAliasMap.size > 0;
398
- if (hasAny) {
399
- const constDecl = Array.from(constMap.values())
400
- .map(c => `export declare const ${c.name}: ${c.type};`)
401
- .join('\n');
402
- const enumDecl = enums
403
- .map(e => `export enum ${e.name} { ${e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ')} }`)
404
- .join('\n');
405
- const typeAliasDecl = Array.from(typeAliasMap.values())
406
- .map(t => `export type ${t.name} = ${t.type};`)
407
- .join('\n');
408
- const typesContent = [
409
- '/* Generated by WebF CLI - aggregated type declarations */',
410
- typeAliasDecl,
411
- constDecl,
412
- enumDecl,
413
- ''
414
- ].filter(Boolean).join('\n');
415
- const typesPath = path_1.default.join(normalizedTarget, 'src', 'types.ts');
416
- if (writeFileIfChanged(typesPath, typesContent)) {
417
- filesChanged++;
418
- (0, logger_1.debug)(`Generated: src/types.ts`);
419
- try {
420
- const constNames = Array.from(constMap.keys());
421
- const aliasNames = Array.from(typeAliasMap.keys());
422
- const enumNames = enums.map(e => e.name);
423
- (0, logger_1.debug)(`[react] Aggregated types - consts: ${constNames.join(', ') || '(none)'}; typeAliases: ${aliasNames.join(', ') || '(none)'}; enums: ${enumNames.join(', ') || '(none)'}\n`);
424
- (0, logger_1.debug)(`[react] src/types.ts preview:\n` + typesContent.split('\n').slice(0, 20).join('\n'));
425
- }
426
- catch (_b) { }
395
+ const constDecl = Array.from(constMap.values())
396
+ .map(c => `export declare const ${c.name}: ${c.type};`)
397
+ .join('\n');
398
+ const enumDecl = enums
399
+ .map(e => `export enum ${e.name} { ${e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ')} }`)
400
+ .join('\n');
401
+ const typeAliasDecl = Array.from(typeAliasMap.values())
402
+ .map(t => `export type ${t.name} = ${t.type};`)
403
+ .join('\n');
404
+ const typesContent = [
405
+ '/* Generated by WebF CLI - aggregated type declarations */',
406
+ hasAny ? typeAliasDecl : '',
407
+ hasAny ? constDecl : '',
408
+ hasAny ? enumDecl : '',
409
+ hasAny ? '' : 'export {};',
410
+ ''
411
+ ].filter(Boolean).join('\n');
412
+ const typesPath = path_1.default.join(normalizedTarget, 'src', 'types.ts');
413
+ if (writeFileIfChanged(typesPath, typesContent)) {
414
+ filesChanged++;
415
+ (0, logger_1.debug)(`Generated: src/types.ts`);
416
+ try {
417
+ const constNames = Array.from(constMap.keys());
418
+ const aliasNames = Array.from(typeAliasMap.keys());
419
+ const enumNames = enums.map(e => e.name);
420
+ (0, logger_1.debug)(`[react] Aggregated types - consts: ${constNames.join(', ') || '(none)'}; typeAliases: ${aliasNames.join(', ') || '(none)'}; enums: ${enumNames.join(', ') || '(none)'}\n`);
421
+ (0, logger_1.debug)(`[react] src/types.ts preview:\n` + typesContent.split('\n').slice(0, 20).join('\n'));
427
422
  }
428
- // Ensure index.ts re-exports these types so consumers get them on import.
429
- const indexFilePath = path_1.default.join(normalizedTarget, 'src', 'index.ts');
423
+ catch (_b) { }
424
+ }
425
+ // Only re-export from index.ts when there are actual declarations to surface.
426
+ if (hasAny) {
430
427
  try {
431
428
  let current = '';
432
429
  if (fs_1.default.existsSync(indexFilePath)) {
@@ -447,6 +444,10 @@ function reactGen(_a) {
447
444
  catch (e) {
448
445
  (0, logger_1.warn)('Failed to generate aggregated React types');
449
446
  }
447
+ (0, logger_1.timeEnd)('reactGen');
448
+ (0, logger_1.success)(`React code generation completed. ${filesChanged} files changed.`);
449
+ (0, logger_1.info)(`Output directory: ${normalizedTarget}`);
450
+ (0, logger_1.info)('You can now import these components in your React project.');
450
451
  });
451
452
  }
452
453
  function vueGen(_a) {
package/dist/module.js CHANGED
@@ -247,14 +247,14 @@ function mapTsParamTypeToDart(typeText, optionNames) {
247
247
  }
248
248
  return { dartType: 'dynamic', isByteData: false };
249
249
  }
250
- function mapTsPropertyTypeToDart(type) {
250
+ function mapTsPropertyTypeToDart(type, optional) {
251
251
  switch (type.kind) {
252
252
  case typescript_1.default.SyntaxKind.StringKeyword:
253
- return 'String?';
253
+ return optional ? 'String?' : 'String';
254
254
  case typescript_1.default.SyntaxKind.NumberKeyword:
255
- return 'num?';
255
+ return optional ? 'num?' : 'num';
256
256
  case typescript_1.default.SyntaxKind.BooleanKeyword:
257
- return 'bool?';
257
+ return optional ? 'bool?' : 'bool';
258
258
  default:
259
259
  return 'dynamic';
260
260
  }
@@ -288,28 +288,52 @@ function buildDartBindings(def, command) {
288
288
  continue;
289
289
  const key = member.name.getText(def.sourceFile).replace(/['"]/g, '');
290
290
  const fieldName = key;
291
- const dartType = member.type ? mapTsPropertyTypeToDart(member.type) : 'dynamic';
292
- propInfos.push({ fieldName, key, dartType });
291
+ const optional = !!member.questionToken;
292
+ const dartType = member.type ? mapTsPropertyTypeToDart(member.type, optional) : 'dynamic';
293
+ propInfos.push({ fieldName, key, dartType, optional });
293
294
  }
294
295
  lines.push(`class ${name} {`);
295
296
  for (const prop of propInfos) {
296
297
  lines.push(` final ${prop.dartType} ${prop.fieldName};`);
297
298
  }
298
299
  lines.push('');
299
- const ctorParams = propInfos.map(p => `this.${p.fieldName}`).join(', ');
300
+ const ctorParams = propInfos.map(p => {
301
+ if (p.optional || p.dartType === 'dynamic') {
302
+ return `this.${p.fieldName}`;
303
+ }
304
+ return `required this.${p.fieldName}`;
305
+ }).join(', ');
300
306
  lines.push(` const ${name}({${ctorParams}});`);
301
307
  lines.push('');
302
308
  lines.push(` factory ${name}.fromMap(Map<String, dynamic> map) {`);
303
309
  lines.push(` return ${name}(`);
304
310
  for (const prop of propInfos) {
305
- if (prop.dartType.startsWith('String')) {
306
- lines.push(` ${prop.fieldName}: map['${prop.key}']?.toString(),`);
311
+ const isString = prop.dartType.startsWith('String');
312
+ const isBool = prop.dartType.startsWith('bool');
313
+ const isNum = prop.dartType.startsWith('num');
314
+ if (isString) {
315
+ if (prop.optional) {
316
+ lines.push(` ${prop.fieldName}: map['${prop.key}']?.toString(),`);
317
+ }
318
+ else {
319
+ lines.push(` ${prop.fieldName}: map['${prop.key}']?.toString() ?? '',`);
320
+ }
307
321
  }
308
- else if (prop.dartType.startsWith('bool')) {
309
- lines.push(` ${prop.fieldName}: map['${prop.key}'] is bool ? map['${prop.key}'] as bool : null,`);
322
+ else if (isBool) {
323
+ if (prop.optional) {
324
+ lines.push(` ${prop.fieldName}: map['${prop.key}'] is bool ? map['${prop.key}'] as bool : null,`);
325
+ }
326
+ else {
327
+ lines.push(` ${prop.fieldName}: map['${prop.key}'] is bool ? map['${prop.key}'] as bool : false,`);
328
+ }
310
329
  }
311
- else if (prop.dartType.startsWith('num')) {
312
- lines.push(` ${prop.fieldName}: map['${prop.key}'] is num ? map['${prop.key}'] as num : null,`);
330
+ else if (isNum) {
331
+ if (prop.optional) {
332
+ lines.push(` ${prop.fieldName}: map['${prop.key}'] is num ? map['${prop.key}'] as num : null,`);
333
+ }
334
+ else {
335
+ lines.push(` ${prop.fieldName}: map['${prop.key}'] is num ? map['${prop.key}'] as num : 0,`);
336
+ }
313
337
  }
314
338
  else {
315
339
  lines.push(` ${prop.fieldName}: map['${prop.key}'],`);
@@ -321,7 +345,12 @@ function buildDartBindings(def, command) {
321
345
  lines.push(' Map<String, dynamic> toMap() {');
322
346
  lines.push(' final map = <String, dynamic>{};');
323
347
  for (const prop of propInfos) {
324
- lines.push(` if (${prop.fieldName} != null) { map['${prop.key}'] = ${prop.fieldName}; }`);
348
+ if (!prop.optional && (prop.dartType === 'String' || prop.dartType === 'bool' || prop.dartType === 'num')) {
349
+ lines.push(` map['${prop.key}'] = ${prop.fieldName};`);
350
+ }
351
+ else {
352
+ lines.push(` if (${prop.fieldName} != null) { map['${prop.key}'] = ${prop.fieldName}; }`);
353
+ }
325
354
  }
326
355
  lines.push(' return map;');
327
356
  lines.push(' }');