@openwebf/webf 0.23.2 → 0.23.10

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/commands.js CHANGED
@@ -13,11 +13,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.generateCommand = generateCommand;
16
+ exports.generateModuleCommand = generateModuleCommand;
16
17
  const child_process_1 = require("child_process");
17
18
  const fs_1 = __importDefault(require("fs"));
18
19
  const path_1 = __importDefault(require("path"));
19
20
  const os_1 = __importDefault(require("os"));
20
21
  const generator_1 = require("./generator");
22
+ const module_1 = require("./module");
21
23
  const glob_1 = require("glob");
22
24
  const lodash_1 = __importDefault(require("lodash"));
23
25
  const inquirer_1 = __importDefault(require("inquirer"));
@@ -141,6 +143,9 @@ const NPM = platform == 'win32' ? 'npm.cmd' : 'npm';
141
143
  const gloabalDts = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../global.d.ts'), 'utf-8');
142
144
  const tsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/tsconfig.json.tpl'), 'utf-8');
143
145
  const gitignore = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/gitignore.tpl'), 'utf-8');
146
+ const modulePackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.package.json.tpl'), 'utf-8');
147
+ const moduleTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.tsconfig.json.tpl'), 'utf-8');
148
+ const moduleTsUpConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.tsup.config.ts.tpl'), 'utf-8');
144
149
  const reactPackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.package.json.tpl'), 'utf-8');
145
150
  const reactTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsconfig.json.tpl'), 'utf-8');
146
151
  const reactTsUpConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsup.config.ts.tpl'), 'utf-8');
@@ -172,7 +177,8 @@ function readFlutterPackageMetadata(packagePath) {
172
177
  return null;
173
178
  }
174
179
  }
175
- // Copy markdown docs that match .d.ts basenames from source to the built dist folder
180
+ // Copy markdown docs that match .d.ts basenames from source to the built dist folder,
181
+ // and generate an aggregated README.md in the dist directory.
176
182
  function copyMarkdownDocsToDist(params) {
177
183
  return __awaiter(this, void 0, void 0, function* () {
178
184
  const { sourceRoot, distRoot, exclude } = params;
@@ -184,9 +190,10 @@ function copyMarkdownDocsToDist(params) {
184
190
  const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
185
191
  const ignore = exclude && exclude.length ? [...defaultIgnore, ...exclude] : defaultIgnore;
186
192
  // Find all .d.ts files and check for sibling .md files
187
- const dtsFiles = glob_1.glob.globSync('**/*.d.ts', { cwd: sourceRoot, ignore });
193
+ const dtsFiles = (0, glob_1.globSync)('**/*.d.ts', { cwd: sourceRoot, ignore });
188
194
  let copied = 0;
189
195
  let skipped = 0;
196
+ const readmeSections = [];
190
197
  for (const relDts of dtsFiles) {
191
198
  if (path_1.default.basename(relDts) === 'global.d.ts') {
192
199
  continue;
@@ -197,6 +204,13 @@ function copyMarkdownDocsToDist(params) {
197
204
  skipped++;
198
205
  continue;
199
206
  }
207
+ let content = '';
208
+ try {
209
+ content = fs_1.default.readFileSync(absMd, 'utf-8');
210
+ }
211
+ catch (_a) {
212
+ // If we cannot read the file, still attempt to copy it and skip README aggregation for this entry.
213
+ }
200
214
  // Copy into dist preserving relative path
201
215
  const destPath = path_1.default.join(distRoot, relMd);
202
216
  const destDir = path_1.default.dirname(destPath);
@@ -205,6 +219,61 @@ function copyMarkdownDocsToDist(params) {
205
219
  }
206
220
  fs_1.default.copyFileSync(absMd, destPath);
207
221
  copied++;
222
+ if (content) {
223
+ const base = path_1.default.basename(relMd, '.md');
224
+ const title = base
225
+ .split(/[-_]+/)
226
+ .filter(Boolean)
227
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
228
+ .join(' ');
229
+ readmeSections.push({
230
+ title: title || base,
231
+ relPath: relMd,
232
+ content
233
+ });
234
+ }
235
+ }
236
+ // Generate an aggregated README.md inside distRoot so consumers can see component docs easily.
237
+ if (readmeSections.length > 0) {
238
+ const readmePath = path_1.default.join(distRoot, 'README.md');
239
+ let existing = '';
240
+ if (fs_1.default.existsSync(readmePath)) {
241
+ try {
242
+ existing = fs_1.default.readFileSync(readmePath, 'utf-8');
243
+ }
244
+ catch (_b) {
245
+ existing = '';
246
+ }
247
+ }
248
+ const headerLines = [
249
+ '# WebF Component Documentation',
250
+ '',
251
+ '> This README is generated from markdown docs co-located with TypeScript definitions in the Flutter package.',
252
+ ''
253
+ ];
254
+ const sectionBlocks = readmeSections.map(section => {
255
+ const lines = [];
256
+ lines.push(`## ${section.title}`);
257
+ lines.push('');
258
+ lines.push(`_Source: \`./${section.relPath}\`_`);
259
+ lines.push('');
260
+ lines.push(section.content.trim());
261
+ lines.push('');
262
+ return lines.join('\n');
263
+ }).join('\n');
264
+ let finalContent;
265
+ if (existing && existing.trim().length > 0) {
266
+ finalContent = `${existing.trim()}\n\n---\n\n${headerLines.join('\n')}${sectionBlocks}\n`;
267
+ }
268
+ else {
269
+ finalContent = `${headerLines.join('\n')}${sectionBlocks}\n`;
270
+ }
271
+ try {
272
+ fs_1.default.writeFileSync(readmePath, finalContent, 'utf-8');
273
+ }
274
+ catch (_c) {
275
+ // If README generation fails, do not affect overall codegen.
276
+ }
208
277
  }
209
278
  return { copied, skipped };
210
279
  });
@@ -311,18 +380,57 @@ function createCommand(target, options) {
311
380
  }
312
381
  console.log(`WebF ${framework} package created at: ${target}`);
313
382
  }
383
+ function createModuleProject(target, options) {
384
+ const { metadata, skipGitignore } = options;
385
+ const packageName = isValidNpmPackageName(options.packageName)
386
+ ? options.packageName
387
+ : sanitizePackageName(options.packageName);
388
+ if (!fs_1.default.existsSync(target)) {
389
+ fs_1.default.mkdirSync(target, { recursive: true });
390
+ }
391
+ const packageJsonPath = path_1.default.join(target, 'package.json');
392
+ const packageJsonContent = lodash_1.default.template(modulePackageJson)({
393
+ packageName,
394
+ version: (metadata === null || metadata === void 0 ? void 0 : metadata.version) || '0.0.1',
395
+ description: (metadata === null || metadata === void 0 ? void 0 : metadata.description) || '',
396
+ });
397
+ writeFileIfChanged(packageJsonPath, packageJsonContent);
398
+ const tsConfigPath = path_1.default.join(target, 'tsconfig.json');
399
+ const tsConfigContent = lodash_1.default.template(moduleTsConfig)({});
400
+ writeFileIfChanged(tsConfigPath, tsConfigContent);
401
+ const tsupConfigPath = path_1.default.join(target, 'tsup.config.ts');
402
+ const tsupConfigContent = lodash_1.default.template(moduleTsUpConfig)({});
403
+ writeFileIfChanged(tsupConfigPath, tsupConfigContent);
404
+ if (!skipGitignore) {
405
+ const gitignorePath = path_1.default.join(target, '.gitignore');
406
+ const gitignoreContent = lodash_1.default.template(gitignore)({});
407
+ writeFileIfChanged(gitignorePath, gitignoreContent);
408
+ }
409
+ const srcDir = path_1.default.join(target, 'src');
410
+ if (!fs_1.default.existsSync(srcDir)) {
411
+ fs_1.default.mkdirSync(srcDir, { recursive: true });
412
+ }
413
+ console.log(`WebF module package scaffold created at: ${target}`);
414
+ }
314
415
  function generateCommand(distPath, options) {
315
416
  return __awaiter(this, void 0, void 0, function* () {
316
417
  var _a, _b, _c, _d;
317
418
  // If distPath is not provided or is '.', create a temporary directory
318
419
  let resolvedDistPath;
319
420
  let isTempDir = false;
421
+ const isDartOnly = options.dartOnly;
320
422
  if (!distPath || distPath === '.') {
321
- // Create a temporary directory for the generated package
322
- const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'webf-typings-'));
323
- resolvedDistPath = tempDir;
324
- isTempDir = true;
325
- console.log(`\nUsing temporary directory: ${tempDir}`);
423
+ if (isDartOnly) {
424
+ // In Dart-only mode we don't need a temporary Node project directory
425
+ resolvedDistPath = path_1.default.resolve(distPath || '.');
426
+ }
427
+ else {
428
+ // Create a temporary directory for the generated package
429
+ const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'webf-typings-'));
430
+ resolvedDistPath = tempDir;
431
+ isTempDir = true;
432
+ console.log(`\nUsing temporary directory: ${tempDir}`);
433
+ }
326
434
  }
327
435
  else {
328
436
  resolvedDistPath = path_1.default.resolve(distPath);
@@ -352,111 +460,123 @@ function generateCommand(distPath, options) {
352
460
  console.log(`\nDetected Flutter package at: ${pubspecDir}`);
353
461
  }
354
462
  }
355
- // Check if the directory exists and has required files
356
- const packageJsonPath = path_1.default.join(resolvedDistPath, 'package.json');
357
- const globalDtsPath = path_1.default.join(resolvedDistPath, 'global.d.ts');
358
- const tsConfigPath = path_1.default.join(resolvedDistPath, 'tsconfig.json');
359
- const hasPackageJson = fs_1.default.existsSync(packageJsonPath);
360
- const hasGlobalDts = fs_1.default.existsSync(globalDtsPath);
361
- const hasTsConfig = fs_1.default.existsSync(tsConfigPath);
362
- // Determine if we need to create a new project
363
- const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
364
- // Track if this is an existing project (has all required files)
365
- const isExistingProject = hasPackageJson && hasGlobalDts && hasTsConfig;
366
463
  let framework = options.framework;
367
464
  let packageName = options.packageName;
368
- // Validate and sanitize package name if provided
369
- if (packageName && !isValidNpmPackageName(packageName)) {
370
- console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
371
- const sanitized = sanitizePackageName(packageName);
372
- console.log(`Using sanitized name: "${sanitized}"`);
373
- packageName = sanitized;
374
- }
375
- if (needsProjectCreation) {
376
- // If project needs creation but options are missing, prompt for them
377
- if (!framework) {
378
- const frameworkAnswer = yield inquirer_1.default.prompt([{
379
- type: 'list',
380
- name: 'framework',
381
- message: 'Which framework would you like to use?',
382
- choices: ['react', 'vue']
383
- }]);
384
- framework = frameworkAnswer.framework;
385
- }
386
- // Try to read Flutter package metadata if flutterPackageSrc is provided
387
- let metadata = null;
388
- if (options.flutterPackageSrc) {
389
- metadata = readFlutterPackageMetadata(options.flutterPackageSrc);
390
- }
391
- if (!packageName) {
392
- // Use Flutter package name as default if available, sanitized for npm
393
- const rawDefaultName = (metadata === null || metadata === void 0 ? void 0 : metadata.name) || path_1.default.basename(resolvedDistPath);
394
- const defaultPackageName = sanitizePackageName(rawDefaultName);
395
- const packageNameAnswer = yield inquirer_1.default.prompt([{
396
- type: 'input',
397
- name: 'packageName',
398
- message: 'What is your package name?',
399
- default: defaultPackageName,
400
- validate: (input) => {
401
- if (!input || input.trim() === '') {
402
- return 'Package name is required';
403
- }
404
- // Check if it's valid as-is
405
- if (isValidNpmPackageName(input)) {
406
- return true;
465
+ let isExistingProject = false;
466
+ if (!isDartOnly) {
467
+ // Check if the directory exists and has required files
468
+ const packageJsonPath = path_1.default.join(resolvedDistPath, 'package.json');
469
+ const globalDtsPath = path_1.default.join(resolvedDistPath, 'global.d.ts');
470
+ const tsConfigPath = path_1.default.join(resolvedDistPath, 'tsconfig.json');
471
+ const hasPackageJson = fs_1.default.existsSync(packageJsonPath);
472
+ const hasGlobalDts = fs_1.default.existsSync(globalDtsPath);
473
+ const hasTsConfig = fs_1.default.existsSync(tsConfigPath);
474
+ // Determine if we need to create a new project
475
+ const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
476
+ // Track if this is an existing project (has all required files)
477
+ isExistingProject = hasPackageJson && hasGlobalDts && hasTsConfig;
478
+ // Validate and sanitize package name if provided
479
+ if (packageName && !isValidNpmPackageName(packageName)) {
480
+ console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
481
+ const sanitized = sanitizePackageName(packageName);
482
+ console.log(`Using sanitized name: "${sanitized}"`);
483
+ packageName = sanitized;
484
+ }
485
+ if (needsProjectCreation) {
486
+ // If project needs creation but options are missing, prompt for them
487
+ if (!framework) {
488
+ const frameworkAnswer = yield inquirer_1.default.prompt([{
489
+ type: 'list',
490
+ name: 'framework',
491
+ message: 'Which framework would you like to use?',
492
+ choices: ['react', 'vue']
493
+ }]);
494
+ framework = frameworkAnswer.framework;
495
+ }
496
+ // Try to read Flutter package metadata if flutterPackageSrc is provided
497
+ let metadata = null;
498
+ if (options.flutterPackageSrc) {
499
+ metadata = readFlutterPackageMetadata(options.flutterPackageSrc);
500
+ }
501
+ if (!packageName) {
502
+ // Use Flutter package name as default if available, sanitized for npm
503
+ const rawDefaultName = (metadata === null || metadata === void 0 ? void 0 : metadata.name) || path_1.default.basename(resolvedDistPath);
504
+ const defaultPackageName = sanitizePackageName(rawDefaultName);
505
+ const packageNameAnswer = yield inquirer_1.default.prompt([{
506
+ type: 'input',
507
+ name: 'packageName',
508
+ message: 'What is your package name?',
509
+ default: defaultPackageName,
510
+ validate: (input) => {
511
+ if (!input || input.trim() === '') {
512
+ return 'Package name is required';
513
+ }
514
+ // Check if it's valid as-is
515
+ if (isValidNpmPackageName(input)) {
516
+ return true;
517
+ }
518
+ // If not valid, show what it would be sanitized to
519
+ const sanitized = sanitizePackageName(input);
520
+ return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
407
521
  }
408
- // If not valid, show what it would be sanitized to
409
- const sanitized = sanitizePackageName(input);
410
- return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
411
- }
412
- }]);
413
- packageName = packageNameAnswer.packageName;
522
+ }]);
523
+ packageName = packageNameAnswer.packageName;
524
+ }
525
+ console.log(`\nCreating new ${framework} project in ${resolvedDistPath}...`);
526
+ createCommand(resolvedDistPath, {
527
+ framework: framework,
528
+ packageName: packageName,
529
+ metadata: metadata || undefined
530
+ });
414
531
  }
415
- console.log(`\nCreating new ${framework} project in ${resolvedDistPath}...`);
416
- createCommand(resolvedDistPath, {
417
- framework: framework,
418
- packageName: packageName,
419
- metadata: metadata || undefined
420
- });
421
- }
422
- else {
423
- // Validate existing project structure
424
- if (hasPackageJson) {
425
- try {
426
- const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
427
- // Detect framework from existing package.json
428
- if (!framework) {
429
- if (((_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a.react) || ((_b = packageJson.devDependencies) === null || _b === void 0 ? void 0 : _b.react)) {
430
- framework = 'react';
431
- }
432
- else if (((_c = packageJson.dependencies) === null || _c === void 0 ? void 0 : _c.vue) || ((_d = packageJson.devDependencies) === null || _d === void 0 ? void 0 : _d.vue)) {
433
- framework = 'vue';
434
- }
435
- else {
436
- // If can't detect, prompt for it
437
- const frameworkAnswer = yield inquirer_1.default.prompt([{
438
- type: 'list',
439
- name: 'framework',
440
- message: 'Which framework are you using?',
441
- choices: ['react', 'vue']
442
- }]);
443
- framework = frameworkAnswer.framework;
532
+ else {
533
+ // Validate existing project structure
534
+ if (hasPackageJson) {
535
+ try {
536
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
537
+ // Detect framework from existing package.json
538
+ if (!framework) {
539
+ if (((_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a.react) || ((_b = packageJson.devDependencies) === null || _b === void 0 ? void 0 : _b.react)) {
540
+ framework = 'react';
541
+ }
542
+ else if (((_c = packageJson.dependencies) === null || _c === void 0 ? void 0 : _c.vue) || ((_d = packageJson.devDependencies) === null || _d === void 0 ? void 0 : _d.vue)) {
543
+ framework = 'vue';
544
+ }
545
+ else {
546
+ // If can't detect, prompt for it
547
+ const frameworkAnswer = yield inquirer_1.default.prompt([{
548
+ type: 'list',
549
+ name: 'framework',
550
+ message: 'Which framework are you using?',
551
+ choices: ['react', 'vue']
552
+ }]);
553
+ framework = frameworkAnswer.framework;
554
+ }
444
555
  }
556
+ console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
557
+ }
558
+ catch (e) {
559
+ console.error('Error reading package.json:', e);
560
+ process.exit(1);
445
561
  }
446
- console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
447
- }
448
- catch (e) {
449
- console.error('Error reading package.json:', e);
450
- process.exit(1);
451
562
  }
452
563
  }
453
564
  }
565
+ else {
566
+ // In Dart-only mode, framework/packageName are unused; ensure framework is not accidentally required later.
567
+ framework = options.framework;
568
+ }
454
569
  // Now proceed with code generation if flutter package source is provided
455
570
  if (!options.flutterPackageSrc) {
456
571
  console.log('\nProject is ready for code generation.');
457
572
  console.log('To generate code, run:');
458
573
  const displayPath = isTempDir ? '<output-dir>' : distPath;
459
- console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --framework=${framework}`);
574
+ if (isDartOnly) {
575
+ console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --dart-only`);
576
+ }
577
+ else {
578
+ console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --framework=${framework}`);
579
+ }
460
580
  if (isTempDir) {
461
581
  // Clean up temporary directory if we're not using it
462
582
  fs_1.default.rmSync(resolvedDistPath, { recursive: true, force: true });
@@ -519,7 +639,23 @@ function generateCommand(distPath, options) {
519
639
  process.exit(1);
520
640
  }
521
641
  }
522
- const command = `webf codegen --flutter-package-src=${options.flutterPackageSrc} --framework=${framework} <distPath>`;
642
+ const baseCommand = 'webf codegen';
643
+ const flutterPart = options.flutterPackageSrc ? ` --flutter-package-src=${options.flutterPackageSrc}` : '';
644
+ const modePart = isDartOnly
645
+ ? ' --dart-only'
646
+ : (framework ? ` --framework=${framework}` : '');
647
+ const command = `${baseCommand}${flutterPart}${modePart} <distPath>`;
648
+ if (isDartOnly) {
649
+ console.log(`\nGenerating Dart bindings from ${options.flutterPackageSrc}...`);
650
+ yield (0, generator_1.dartGen)({
651
+ source: options.flutterPackageSrc,
652
+ target: options.flutterPackageSrc,
653
+ command,
654
+ exclude: options.exclude,
655
+ });
656
+ console.log('\nDart code generation completed successfully!');
657
+ return;
658
+ }
523
659
  // Auto-initialize typings in the output directory if needed
524
660
  ensureInitialized(resolvedDistPath);
525
661
  console.log(`\nGenerating ${framework} code from ${options.flutterPackageSrc}...`);
@@ -636,6 +772,208 @@ function generateCommand(distPath, options) {
636
772
  }
637
773
  });
638
774
  }
775
+ function generateModuleCommand(distPath, options) {
776
+ return __awaiter(this, void 0, void 0, function* () {
777
+ let resolvedDistPath;
778
+ let isTempDir = false;
779
+ if (!distPath || distPath === '.') {
780
+ const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'webf-module-'));
781
+ resolvedDistPath = tempDir;
782
+ isTempDir = true;
783
+ console.log(`\nUsing temporary directory for module package: ${tempDir}`);
784
+ }
785
+ else {
786
+ resolvedDistPath = path_1.default.resolve(distPath);
787
+ }
788
+ // Detect Flutter package root if not provided
789
+ if (!options.flutterPackageSrc) {
790
+ let currentDir = process.cwd();
791
+ let foundPubspec = false;
792
+ let pubspecDir = '';
793
+ for (let i = 0; i < 3; i++) {
794
+ const pubspecPath = path_1.default.join(currentDir, 'pubspec.yaml');
795
+ if (fs_1.default.existsSync(pubspecPath)) {
796
+ foundPubspec = true;
797
+ pubspecDir = currentDir;
798
+ break;
799
+ }
800
+ const parentDir = path_1.default.dirname(currentDir);
801
+ if (parentDir === currentDir)
802
+ break;
803
+ currentDir = parentDir;
804
+ }
805
+ if (!foundPubspec) {
806
+ console.error('Could not find pubspec.yaml. Please provide --flutter-package-src.');
807
+ process.exit(1);
808
+ }
809
+ options.flutterPackageSrc = pubspecDir;
810
+ console.log(`Detected Flutter package at: ${pubspecDir}`);
811
+ }
812
+ const flutterPackageSrc = path_1.default.resolve(options.flutterPackageSrc);
813
+ // Validate TS environment in the Flutter package
814
+ console.log(`\nValidating TypeScript environment in ${flutterPackageSrc}...`);
815
+ let validation = validateTypeScriptEnvironment(flutterPackageSrc);
816
+ if (!validation.isValid) {
817
+ const tsConfigPath = path_1.default.join(flutterPackageSrc, 'tsconfig.json');
818
+ if (!fs_1.default.existsSync(tsConfigPath)) {
819
+ const defaultTsConfig = {
820
+ compilerOptions: {
821
+ target: 'ES2020',
822
+ module: 'commonjs',
823
+ lib: ['ES2020'],
824
+ declaration: true,
825
+ strict: true,
826
+ esModuleInterop: true,
827
+ skipLibCheck: true,
828
+ forceConsistentCasingInFileNames: true,
829
+ resolveJsonModule: true,
830
+ moduleResolution: 'node',
831
+ },
832
+ include: ['lib/**/*.d.ts', '**/*.d.ts'],
833
+ exclude: ['node_modules', 'dist', 'build'],
834
+ };
835
+ fs_1.default.writeFileSync(tsConfigPath, JSON.stringify(defaultTsConfig, null, 2), 'utf-8');
836
+ console.log('✅ Created tsconfig.json for module package');
837
+ validation = validateTypeScriptEnvironment(flutterPackageSrc);
838
+ }
839
+ if (!validation.isValid) {
840
+ console.error('\n❌ TypeScript environment validation failed:');
841
+ validation.errors.forEach(err => console.error(` - ${err}`));
842
+ console.error('\nPlease fix the above issues before running `webf module-codegen` again.');
843
+ process.exit(1);
844
+ }
845
+ }
846
+ // Read Flutter metadata for package.json
847
+ const metadata = readFlutterPackageMetadata(flutterPackageSrc);
848
+ // Determine package name
849
+ let packageName = options.packageName;
850
+ if (packageName && !isValidNpmPackageName(packageName)) {
851
+ console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
852
+ const sanitized = sanitizePackageName(packageName);
853
+ console.log(`Using sanitized name: "${sanitized}"`);
854
+ packageName = sanitized;
855
+ }
856
+ if (!packageName) {
857
+ const rawDefaultName = (metadata === null || metadata === void 0 ? void 0 : metadata.name)
858
+ ? `@openwebf/${metadata.name.replace(/^webf_/, 'webf-')}`
859
+ : '@openwebf/webf-module';
860
+ const defaultPackageName = isValidNpmPackageName(rawDefaultName)
861
+ ? rawDefaultName
862
+ : sanitizePackageName(rawDefaultName);
863
+ const packageNameAnswer = yield inquirer_1.default.prompt([{
864
+ type: 'input',
865
+ name: 'packageName',
866
+ message: 'What is your npm package name for this module?',
867
+ default: defaultPackageName,
868
+ validate: (input) => {
869
+ if (!input || input.trim() === '') {
870
+ return 'Package name is required';
871
+ }
872
+ if (isValidNpmPackageName(input)) {
873
+ return true;
874
+ }
875
+ const sanitized = sanitizePackageName(input);
876
+ return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
877
+ }
878
+ }]);
879
+ packageName = packageNameAnswer.packageName;
880
+ }
881
+ // Prevent npm scaffolding (package.json, tsup.config.ts, etc.) from being written into
882
+ // the Flutter package itself. Force users to choose a separate output directory.
883
+ if (resolvedDistPath === flutterPackageSrc) {
884
+ console.error('\n❌ Output directory must not be the Flutter package root.');
885
+ console.error('Please choose a separate directory for the generated npm package, for example:');
886
+ console.error(' webf module-codegen ../packages/webf-share --flutter-package-src=../webf_modules/share');
887
+ process.exit(1);
888
+ }
889
+ // Scaffold npm project for the module
890
+ if (!packageName) {
891
+ throw new Error('Package name could not be resolved for module package.');
892
+ }
893
+ createModuleProject(resolvedDistPath, {
894
+ packageName,
895
+ metadata: metadata || undefined,
896
+ });
897
+ // Locate module interface file (*.module.d.ts)
898
+ const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
899
+ const ignore = options.exclude && options.exclude.length
900
+ ? [...defaultIgnore, ...options.exclude]
901
+ : defaultIgnore;
902
+ const candidates = (0, glob_1.globSync)('**/*.module.d.ts', {
903
+ cwd: flutterPackageSrc,
904
+ ignore,
905
+ });
906
+ if (candidates.length === 0) {
907
+ console.error(`\n❌ No module interface files (*.module.d.ts) found under ${flutterPackageSrc}.`);
908
+ console.error('Please add a TypeScript interface file describing your module API.');
909
+ process.exit(1);
910
+ }
911
+ const moduleInterfaceRel = candidates[0];
912
+ const moduleInterfacePath = path_1.default.join(flutterPackageSrc, moduleInterfaceRel);
913
+ const command = `webf module-codegen --flutter-package-src=${flutterPackageSrc} <distPath>`;
914
+ console.log(`\nGenerating module npm package and Dart bindings from ${moduleInterfaceRel}...`);
915
+ (0, module_1.generateModuleArtifacts)({
916
+ moduleInterfacePath,
917
+ npmTargetDir: resolvedDistPath,
918
+ flutterPackageDir: flutterPackageSrc,
919
+ command,
920
+ });
921
+ console.log('\nModule code generation completed successfully!');
922
+ try {
923
+ yield buildPackage(resolvedDistPath);
924
+ }
925
+ catch (error) {
926
+ console.error('\nWarning: Build failed:', error);
927
+ }
928
+ if (options.publishToNpm) {
929
+ try {
930
+ yield buildAndPublishPackage(resolvedDistPath, options.npmRegistry, false);
931
+ }
932
+ catch (error) {
933
+ console.error('\nError during npm publish:', error);
934
+ process.exit(1);
935
+ }
936
+ }
937
+ else {
938
+ const publishAnswer = yield inquirer_1.default.prompt([{
939
+ type: 'confirm',
940
+ name: 'publish',
941
+ message: 'Would you like to publish this module package to npm?',
942
+ default: false
943
+ }]);
944
+ if (publishAnswer.publish) {
945
+ const registryAnswer = yield inquirer_1.default.prompt([{
946
+ type: 'input',
947
+ name: 'registry',
948
+ message: 'NPM registry URL (leave empty for default npm registry):',
949
+ default: '',
950
+ validate: (input) => {
951
+ if (!input)
952
+ return true;
953
+ try {
954
+ new URL(input);
955
+ return true;
956
+ }
957
+ catch (_a) {
958
+ return 'Please enter a valid URL';
959
+ }
960
+ }
961
+ }]);
962
+ try {
963
+ yield buildAndPublishPackage(resolvedDistPath, registryAnswer.registry || undefined, false);
964
+ }
965
+ catch (error) {
966
+ console.error('\nError during npm publish:', error);
967
+ // Don't exit here since generation was successful
968
+ }
969
+ }
970
+ }
971
+ if (isTempDir) {
972
+ console.log(`\n📁 Generated module npm package is in: ${resolvedDistPath}`);
973
+ console.log('💡 To use it, copy this directory to your packages folder or publish it directly.');
974
+ }
975
+ });
976
+ }
639
977
  function writeFileIfChanged(filePath, content) {
640
978
  if (fs_1.default.existsSync(filePath)) {
641
979
  const oldContent = fs_1.default.readFileSync(filePath, 'utf-8');