@openwebf/webf 0.23.7 ā 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/bin/webf.js +12 -1
- package/dist/commands.js +375 -9
- package/dist/generator.js +39 -37
- package/dist/module.js +487 -0
- package/dist/peerDeps.js +27 -0
- package/dist/react.js +10 -18
- package/dist/vue.js +138 -117
- package/package.json +2 -2
- package/src/commands.ts +441 -11
- package/src/generator.ts +41 -40
- package/src/module.ts +632 -0
- package/src/peerDeps.ts +21 -0
- package/src/react.ts +10 -18
- package/src/vue.ts +158 -128
- package/templates/module.package.json.tpl +36 -0
- package/templates/module.tsconfig.json.tpl +25 -0
- package/templates/module.tsup.config.ts.tpl +13 -0
- package/templates/react.component.tsx.tpl +2 -2
- package/templates/react.index.ts.tpl +2 -1
- package/templates/react.package.json.tpl +4 -5
- package/templates/react.tsconfig.json.tpl +8 -1
- package/templates/react.tsup.config.ts.tpl +1 -1
- package/templates/vue.component.partial.tpl +4 -4
- package/templates/vue.components.d.ts.tpl +24 -9
- package/templates/vue.package.json.tpl +4 -2
- package/test/commands.test.ts +86 -19
- package/test/generator.test.ts +33 -24
- package/test/peerDeps.test.ts +30 -0
- package/test/react-consts.test.ts +9 -3
- package/test/standard-props.test.ts +14 -14
- package/test/templates.test.ts +17 -0
- package/test/vue.test.ts +36 -11
- package/dist/constants.js +0 -242
package/README.md
CHANGED
|
@@ -64,6 +64,34 @@ webf codegen my-typings --flutter-package-src=../webf_ui --publish-to-npm
|
|
|
64
64
|
webf codegen my-typings --flutter-package-src=../webf_ui --publish-to-npm --npm-registry=https://custom.registry.com/
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### Generate Module Packages
|
|
68
|
+
|
|
69
|
+
The `webf module-codegen` command generates a typed npm package and Dart bindings for a WebF module based on a TypeScript interface file (`*.module.d.ts`) in your Flutter package.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
webf module-codegen [output-dir] [options]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Options:
|
|
76
|
+
- `--flutter-package-src <path>`: Flutter module package path containing `*.module.d.ts`
|
|
77
|
+
- `--package-name <name>`: NPM package name for the module (defaults to a name derived from `pubspec.yaml`)
|
|
78
|
+
- `--publish-to-npm`: Automatically publish the generated package to npm
|
|
79
|
+
- `--npm-registry <url>`: Custom npm registry URL (defaults to https://registry.npmjs.org/)
|
|
80
|
+
- `--exclude <patterns...>`: Additional glob patterns to exclude from scanning (e.g. build directories)
|
|
81
|
+
|
|
82
|
+
#### Example:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# From the CLI repo root
|
|
86
|
+
webf module-codegen ../packages/webf-share --flutter-package-src=../webf_modules/share --package-name=@openwebf/webf-share
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This will:
|
|
90
|
+
- Scaffold an npm package at `../packages/webf-share`
|
|
91
|
+
- Read `*.module.d.ts` from `../webf_modules/share`
|
|
92
|
+
- Generate `src/index.ts` and `src/types.ts` that wrap `webf.invokeModuleAsync('Share', ...)`
|
|
93
|
+
- Generate Dart bindings in `../webf_modules/share/lib/src/share_module_bindings_generated.dart`
|
|
94
|
+
|
|
67
95
|
### Interactive Mode
|
|
68
96
|
|
|
69
97
|
If you don't provide all required options, the CLI will prompt you interactively:
|
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 } = require('../dist/commands');
|
|
5
|
+
const { generateCommand, generateModuleCommand } = require('../dist/commands');
|
|
6
6
|
|
|
7
7
|
const program = new Command();
|
|
8
8
|
|
|
@@ -24,4 +24,15 @@ program
|
|
|
24
24
|
.description('Generate dart abstract classes and React/Vue components (auto-creates project if needed)')
|
|
25
25
|
.action(generateCommand);
|
|
26
26
|
|
|
27
|
+
program
|
|
28
|
+
.command('module-codegen')
|
|
29
|
+
.option('--flutter-package-src <src>', 'Flutter module package source path (for module code generation)')
|
|
30
|
+
.option('--package-name <name>', 'NPM package name for the WebF module')
|
|
31
|
+
.option('--publish-to-npm', 'Automatically publish the generated module package to npm')
|
|
32
|
+
.option('--npm-registry <url>', 'Custom npm registry URL (defaults to https://registry.npmjs.org/)')
|
|
33
|
+
.option('--exclude <patterns...>', 'Additional glob patterns to exclude from code generation')
|
|
34
|
+
.argument('[distPath]', 'Path to output generated files', '.')
|
|
35
|
+
.description('Generate NPM package and Dart bindings for a WebF module from TypeScript interfaces (*.module.d.ts)')
|
|
36
|
+
.action(generateModuleCommand);
|
|
37
|
+
|
|
27
38
|
program.parse();
|
package/dist/commands.js
CHANGED
|
@@ -13,11 +13,14 @@ 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");
|
|
23
|
+
const peerDeps_1 = require("./peerDeps");
|
|
21
24
|
const glob_1 = require("glob");
|
|
22
25
|
const lodash_1 = __importDefault(require("lodash"));
|
|
23
26
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
@@ -141,6 +144,9 @@ const NPM = platform == 'win32' ? 'npm.cmd' : 'npm';
|
|
|
141
144
|
const gloabalDts = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../global.d.ts'), 'utf-8');
|
|
142
145
|
const tsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/tsconfig.json.tpl'), 'utf-8');
|
|
143
146
|
const gitignore = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/gitignore.tpl'), 'utf-8');
|
|
147
|
+
const modulePackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.package.json.tpl'), 'utf-8');
|
|
148
|
+
const moduleTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.tsconfig.json.tpl'), 'utf-8');
|
|
149
|
+
const moduleTsUpConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/module.tsup.config.ts.tpl'), 'utf-8');
|
|
144
150
|
const reactPackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.package.json.tpl'), 'utf-8');
|
|
145
151
|
const reactTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsconfig.json.tpl'), 'utf-8');
|
|
146
152
|
const reactTsUpConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsup.config.ts.tpl'), 'utf-8');
|
|
@@ -172,6 +178,33 @@ function readFlutterPackageMetadata(packagePath) {
|
|
|
172
178
|
return null;
|
|
173
179
|
}
|
|
174
180
|
}
|
|
181
|
+
function copyReadmeToPackageRoot(params) {
|
|
182
|
+
const { sourceRoot, targetRoot } = params;
|
|
183
|
+
const targetPath = path_1.default.join(targetRoot, 'README.md');
|
|
184
|
+
if (fs_1.default.existsSync(targetPath)) {
|
|
185
|
+
return { copied: false, targetPath };
|
|
186
|
+
}
|
|
187
|
+
const candidateNames = ['README.md', 'Readme.md', 'readme.md'];
|
|
188
|
+
let sourcePath = null;
|
|
189
|
+
for (const candidate of candidateNames) {
|
|
190
|
+
const abs = path_1.default.join(sourceRoot, candidate);
|
|
191
|
+
if (fs_1.default.existsSync(abs)) {
|
|
192
|
+
sourcePath = abs;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (!sourcePath) {
|
|
197
|
+
return { copied: false, targetPath };
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const content = fs_1.default.readFileSync(sourcePath, 'utf-8');
|
|
201
|
+
writeFileIfChanged(targetPath, content);
|
|
202
|
+
return { copied: true, sourcePath, targetPath };
|
|
203
|
+
}
|
|
204
|
+
catch (_a) {
|
|
205
|
+
return { copied: false, targetPath };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
175
208
|
// Copy markdown docs that match .d.ts basenames from source to the built dist folder,
|
|
176
209
|
// and generate an aggregated README.md in the dist directory.
|
|
177
210
|
function copyMarkdownDocsToDist(params) {
|
|
@@ -185,7 +218,7 @@ function copyMarkdownDocsToDist(params) {
|
|
|
185
218
|
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
|
|
186
219
|
const ignore = exclude && exclude.length ? [...defaultIgnore, ...exclude] : defaultIgnore;
|
|
187
220
|
// Find all .d.ts files and check for sibling .md files
|
|
188
|
-
const dtsFiles = glob_1.
|
|
221
|
+
const dtsFiles = (0, glob_1.globSync)('**/*.d.ts', { cwd: sourceRoot, ignore });
|
|
189
222
|
let copied = 0;
|
|
190
223
|
let skipped = 0;
|
|
191
224
|
const readmeSections = [];
|
|
@@ -345,8 +378,8 @@ function createCommand(target, options) {
|
|
|
345
378
|
// Do not overwrite existing index.ts created by the user
|
|
346
379
|
// Leave merge to the codegen step which appends exports safely
|
|
347
380
|
}
|
|
348
|
-
//
|
|
349
|
-
(0, child_process_1.spawnSync)(NPM, ['install'], {
|
|
381
|
+
// Ensure devDependencies are installed even if the user's shell has NODE_ENV=production.
|
|
382
|
+
(0, child_process_1.spawnSync)(NPM, ['install', '--production=false'], {
|
|
350
383
|
cwd: target,
|
|
351
384
|
stdio: 'inherit'
|
|
352
385
|
});
|
|
@@ -365,17 +398,46 @@ function createCommand(target, options) {
|
|
|
365
398
|
const gitignorePath = path_1.default.join(target, '.gitignore');
|
|
366
399
|
const gitignoreContent = lodash_1.default.template(gitignore)({});
|
|
367
400
|
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
stdio: 'inherit'
|
|
371
|
-
});
|
|
372
|
-
(0, child_process_1.spawnSync)(NPM, ['install', 'vue', '-D'], {
|
|
401
|
+
// Ensure devDependencies are installed even if the user's shell has NODE_ENV=production.
|
|
402
|
+
(0, child_process_1.spawnSync)(NPM, ['install', '--production=false'], {
|
|
373
403
|
cwd: target,
|
|
374
404
|
stdio: 'inherit'
|
|
375
405
|
});
|
|
376
406
|
}
|
|
377
407
|
console.log(`WebF ${framework} package created at: ${target}`);
|
|
378
408
|
}
|
|
409
|
+
function createModuleProject(target, options) {
|
|
410
|
+
const { metadata, skipGitignore } = options;
|
|
411
|
+
const packageName = isValidNpmPackageName(options.packageName)
|
|
412
|
+
? options.packageName
|
|
413
|
+
: sanitizePackageName(options.packageName);
|
|
414
|
+
if (!fs_1.default.existsSync(target)) {
|
|
415
|
+
fs_1.default.mkdirSync(target, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
const packageJsonPath = path_1.default.join(target, 'package.json');
|
|
418
|
+
const packageJsonContent = lodash_1.default.template(modulePackageJson)({
|
|
419
|
+
packageName,
|
|
420
|
+
version: (metadata === null || metadata === void 0 ? void 0 : metadata.version) || '0.0.1',
|
|
421
|
+
description: (metadata === null || metadata === void 0 ? void 0 : metadata.description) || '',
|
|
422
|
+
});
|
|
423
|
+
writeFileIfChanged(packageJsonPath, packageJsonContent);
|
|
424
|
+
const tsConfigPath = path_1.default.join(target, 'tsconfig.json');
|
|
425
|
+
const tsConfigContent = lodash_1.default.template(moduleTsConfig)({});
|
|
426
|
+
writeFileIfChanged(tsConfigPath, tsConfigContent);
|
|
427
|
+
const tsupConfigPath = path_1.default.join(target, 'tsup.config.ts');
|
|
428
|
+
const tsupConfigContent = lodash_1.default.template(moduleTsUpConfig)({});
|
|
429
|
+
writeFileIfChanged(tsupConfigPath, tsupConfigContent);
|
|
430
|
+
if (!skipGitignore) {
|
|
431
|
+
const gitignorePath = path_1.default.join(target, '.gitignore');
|
|
432
|
+
const gitignoreContent = lodash_1.default.template(gitignore)({});
|
|
433
|
+
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
434
|
+
}
|
|
435
|
+
const srcDir = path_1.default.join(target, 'src');
|
|
436
|
+
if (!fs_1.default.existsSync(srcDir)) {
|
|
437
|
+
fs_1.default.mkdirSync(srcDir, { recursive: true });
|
|
438
|
+
}
|
|
439
|
+
console.log(`WebF module package scaffold created at: ${target}`);
|
|
440
|
+
}
|
|
379
441
|
function generateCommand(distPath, options) {
|
|
380
442
|
return __awaiter(this, void 0, void 0, function* () {
|
|
381
443
|
var _a, _b, _c, _d;
|
|
@@ -622,6 +684,16 @@ function generateCommand(distPath, options) {
|
|
|
622
684
|
}
|
|
623
685
|
// Auto-initialize typings in the output directory if needed
|
|
624
686
|
ensureInitialized(resolvedDistPath);
|
|
687
|
+
// Copy README.md from the source Flutter package into the npm package root (so `npm publish` includes it).
|
|
688
|
+
if (options.flutterPackageSrc) {
|
|
689
|
+
const { copied } = copyReadmeToPackageRoot({
|
|
690
|
+
sourceRoot: options.flutterPackageSrc,
|
|
691
|
+
targetRoot: resolvedDistPath,
|
|
692
|
+
});
|
|
693
|
+
if (copied) {
|
|
694
|
+
console.log('š Copied README.md to package root');
|
|
695
|
+
}
|
|
696
|
+
}
|
|
625
697
|
console.log(`\nGenerating ${framework} code from ${options.flutterPackageSrc}...`);
|
|
626
698
|
yield (0, generator_1.dartGen)({
|
|
627
699
|
source: options.flutterPackageSrc,
|
|
@@ -736,6 +808,208 @@ function generateCommand(distPath, options) {
|
|
|
736
808
|
}
|
|
737
809
|
});
|
|
738
810
|
}
|
|
811
|
+
function generateModuleCommand(distPath, options) {
|
|
812
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
813
|
+
let resolvedDistPath;
|
|
814
|
+
let isTempDir = false;
|
|
815
|
+
if (!distPath || distPath === '.') {
|
|
816
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'webf-module-'));
|
|
817
|
+
resolvedDistPath = tempDir;
|
|
818
|
+
isTempDir = true;
|
|
819
|
+
console.log(`\nUsing temporary directory for module package: ${tempDir}`);
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
resolvedDistPath = path_1.default.resolve(distPath);
|
|
823
|
+
}
|
|
824
|
+
// Detect Flutter package root if not provided
|
|
825
|
+
if (!options.flutterPackageSrc) {
|
|
826
|
+
let currentDir = process.cwd();
|
|
827
|
+
let foundPubspec = false;
|
|
828
|
+
let pubspecDir = '';
|
|
829
|
+
for (let i = 0; i < 3; i++) {
|
|
830
|
+
const pubspecPath = path_1.default.join(currentDir, 'pubspec.yaml');
|
|
831
|
+
if (fs_1.default.existsSync(pubspecPath)) {
|
|
832
|
+
foundPubspec = true;
|
|
833
|
+
pubspecDir = currentDir;
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
837
|
+
if (parentDir === currentDir)
|
|
838
|
+
break;
|
|
839
|
+
currentDir = parentDir;
|
|
840
|
+
}
|
|
841
|
+
if (!foundPubspec) {
|
|
842
|
+
console.error('Could not find pubspec.yaml. Please provide --flutter-package-src.');
|
|
843
|
+
process.exit(1);
|
|
844
|
+
}
|
|
845
|
+
options.flutterPackageSrc = pubspecDir;
|
|
846
|
+
console.log(`Detected Flutter package at: ${pubspecDir}`);
|
|
847
|
+
}
|
|
848
|
+
const flutterPackageSrc = path_1.default.resolve(options.flutterPackageSrc);
|
|
849
|
+
// Validate TS environment in the Flutter package
|
|
850
|
+
console.log(`\nValidating TypeScript environment in ${flutterPackageSrc}...`);
|
|
851
|
+
let validation = validateTypeScriptEnvironment(flutterPackageSrc);
|
|
852
|
+
if (!validation.isValid) {
|
|
853
|
+
const tsConfigPath = path_1.default.join(flutterPackageSrc, 'tsconfig.json');
|
|
854
|
+
if (!fs_1.default.existsSync(tsConfigPath)) {
|
|
855
|
+
const defaultTsConfig = {
|
|
856
|
+
compilerOptions: {
|
|
857
|
+
target: 'ES2020',
|
|
858
|
+
module: 'commonjs',
|
|
859
|
+
lib: ['ES2020'],
|
|
860
|
+
declaration: true,
|
|
861
|
+
strict: true,
|
|
862
|
+
esModuleInterop: true,
|
|
863
|
+
skipLibCheck: true,
|
|
864
|
+
forceConsistentCasingInFileNames: true,
|
|
865
|
+
resolveJsonModule: true,
|
|
866
|
+
moduleResolution: 'node',
|
|
867
|
+
},
|
|
868
|
+
include: ['lib/**/*.d.ts', '**/*.d.ts'],
|
|
869
|
+
exclude: ['node_modules', 'dist', 'build'],
|
|
870
|
+
};
|
|
871
|
+
fs_1.default.writeFileSync(tsConfigPath, JSON.stringify(defaultTsConfig, null, 2), 'utf-8');
|
|
872
|
+
console.log('ā
Created tsconfig.json for module package');
|
|
873
|
+
validation = validateTypeScriptEnvironment(flutterPackageSrc);
|
|
874
|
+
}
|
|
875
|
+
if (!validation.isValid) {
|
|
876
|
+
console.error('\nā TypeScript environment validation failed:');
|
|
877
|
+
validation.errors.forEach(err => console.error(` - ${err}`));
|
|
878
|
+
console.error('\nPlease fix the above issues before running `webf module-codegen` again.');
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
// Read Flutter metadata for package.json
|
|
883
|
+
const metadata = readFlutterPackageMetadata(flutterPackageSrc);
|
|
884
|
+
// Determine package name
|
|
885
|
+
let packageName = options.packageName;
|
|
886
|
+
if (packageName && !isValidNpmPackageName(packageName)) {
|
|
887
|
+
console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
|
|
888
|
+
const sanitized = sanitizePackageName(packageName);
|
|
889
|
+
console.log(`Using sanitized name: "${sanitized}"`);
|
|
890
|
+
packageName = sanitized;
|
|
891
|
+
}
|
|
892
|
+
if (!packageName) {
|
|
893
|
+
const rawDefaultName = (metadata === null || metadata === void 0 ? void 0 : metadata.name)
|
|
894
|
+
? `@openwebf/${metadata.name.replace(/^webf_/, 'webf-')}`
|
|
895
|
+
: '@openwebf/webf-module';
|
|
896
|
+
const defaultPackageName = isValidNpmPackageName(rawDefaultName)
|
|
897
|
+
? rawDefaultName
|
|
898
|
+
: sanitizePackageName(rawDefaultName);
|
|
899
|
+
const packageNameAnswer = yield inquirer_1.default.prompt([{
|
|
900
|
+
type: 'input',
|
|
901
|
+
name: 'packageName',
|
|
902
|
+
message: 'What is your npm package name for this module?',
|
|
903
|
+
default: defaultPackageName,
|
|
904
|
+
validate: (input) => {
|
|
905
|
+
if (!input || input.trim() === '') {
|
|
906
|
+
return 'Package name is required';
|
|
907
|
+
}
|
|
908
|
+
if (isValidNpmPackageName(input)) {
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
const sanitized = sanitizePackageName(input);
|
|
912
|
+
return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
|
|
913
|
+
}
|
|
914
|
+
}]);
|
|
915
|
+
packageName = packageNameAnswer.packageName;
|
|
916
|
+
}
|
|
917
|
+
// Prevent npm scaffolding (package.json, tsup.config.ts, etc.) from being written into
|
|
918
|
+
// the Flutter package itself. Force users to choose a separate output directory.
|
|
919
|
+
if (resolvedDistPath === flutterPackageSrc) {
|
|
920
|
+
console.error('\nā Output directory must not be the Flutter package root.');
|
|
921
|
+
console.error('Please choose a separate directory for the generated npm package, for example:');
|
|
922
|
+
console.error(' webf module-codegen ../packages/webf-share --flutter-package-src=../webf_modules/share');
|
|
923
|
+
process.exit(1);
|
|
924
|
+
}
|
|
925
|
+
// Scaffold npm project for the module
|
|
926
|
+
if (!packageName) {
|
|
927
|
+
throw new Error('Package name could not be resolved for module package.');
|
|
928
|
+
}
|
|
929
|
+
createModuleProject(resolvedDistPath, {
|
|
930
|
+
packageName,
|
|
931
|
+
metadata: metadata || undefined,
|
|
932
|
+
});
|
|
933
|
+
// Locate module interface file (*.module.d.ts)
|
|
934
|
+
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
|
|
935
|
+
const ignore = options.exclude && options.exclude.length
|
|
936
|
+
? [...defaultIgnore, ...options.exclude]
|
|
937
|
+
: defaultIgnore;
|
|
938
|
+
const candidates = (0, glob_1.globSync)('**/*.module.d.ts', {
|
|
939
|
+
cwd: flutterPackageSrc,
|
|
940
|
+
ignore,
|
|
941
|
+
});
|
|
942
|
+
if (candidates.length === 0) {
|
|
943
|
+
console.error(`\nā No module interface files (*.module.d.ts) found under ${flutterPackageSrc}.`);
|
|
944
|
+
console.error('Please add a TypeScript interface file describing your module API.');
|
|
945
|
+
process.exit(1);
|
|
946
|
+
}
|
|
947
|
+
const moduleInterfaceRel = candidates[0];
|
|
948
|
+
const moduleInterfacePath = path_1.default.join(flutterPackageSrc, moduleInterfaceRel);
|
|
949
|
+
const command = `webf module-codegen --flutter-package-src=${flutterPackageSrc} <distPath>`;
|
|
950
|
+
console.log(`\nGenerating module npm package and Dart bindings from ${moduleInterfaceRel}...`);
|
|
951
|
+
(0, module_1.generateModuleArtifacts)({
|
|
952
|
+
moduleInterfacePath,
|
|
953
|
+
npmTargetDir: resolvedDistPath,
|
|
954
|
+
flutterPackageDir: flutterPackageSrc,
|
|
955
|
+
command,
|
|
956
|
+
});
|
|
957
|
+
console.log('\nModule code generation completed successfully!');
|
|
958
|
+
try {
|
|
959
|
+
yield buildPackage(resolvedDistPath);
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
console.error('\nWarning: Build failed:', error);
|
|
963
|
+
}
|
|
964
|
+
if (options.publishToNpm) {
|
|
965
|
+
try {
|
|
966
|
+
yield buildAndPublishPackage(resolvedDistPath, options.npmRegistry, false);
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
console.error('\nError during npm publish:', error);
|
|
970
|
+
process.exit(1);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
const publishAnswer = yield inquirer_1.default.prompt([{
|
|
975
|
+
type: 'confirm',
|
|
976
|
+
name: 'publish',
|
|
977
|
+
message: 'Would you like to publish this module package to npm?',
|
|
978
|
+
default: false
|
|
979
|
+
}]);
|
|
980
|
+
if (publishAnswer.publish) {
|
|
981
|
+
const registryAnswer = yield inquirer_1.default.prompt([{
|
|
982
|
+
type: 'input',
|
|
983
|
+
name: 'registry',
|
|
984
|
+
message: 'NPM registry URL (leave empty for default npm registry):',
|
|
985
|
+
default: '',
|
|
986
|
+
validate: (input) => {
|
|
987
|
+
if (!input)
|
|
988
|
+
return true;
|
|
989
|
+
try {
|
|
990
|
+
new URL(input);
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
catch (_a) {
|
|
994
|
+
return 'Please enter a valid URL';
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}]);
|
|
998
|
+
try {
|
|
999
|
+
yield buildAndPublishPackage(resolvedDistPath, registryAnswer.registry || undefined, false);
|
|
1000
|
+
}
|
|
1001
|
+
catch (error) {
|
|
1002
|
+
console.error('\nError during npm publish:', error);
|
|
1003
|
+
// Don't exit here since generation was successful
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (isTempDir) {
|
|
1008
|
+
console.log(`\nš Generated module npm package is in: ${resolvedDistPath}`);
|
|
1009
|
+
console.log('š” To use it, copy this directory to your packages folder or publish it directly.');
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
739
1013
|
function writeFileIfChanged(filePath, content) {
|
|
740
1014
|
if (fs_1.default.existsSync(filePath)) {
|
|
741
1015
|
const oldContent = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
@@ -765,7 +1039,7 @@ function ensureInitialized(targetPath) {
|
|
|
765
1039
|
}
|
|
766
1040
|
function buildPackage(packagePath) {
|
|
767
1041
|
return __awaiter(this, void 0, void 0, function* () {
|
|
768
|
-
var _a;
|
|
1042
|
+
var _a, _b, _c;
|
|
769
1043
|
const packageJsonPath = path_1.default.join(packagePath, 'package.json');
|
|
770
1044
|
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
771
1045
|
// Skip the error in test environment to avoid console warnings
|
|
@@ -777,6 +1051,91 @@ function buildPackage(packagePath) {
|
|
|
777
1051
|
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
778
1052
|
const packageName = packageJson.name;
|
|
779
1053
|
const packageVersion = packageJson.version;
|
|
1054
|
+
function getInstalledPackageJsonPath(pkgName) {
|
|
1055
|
+
const parts = pkgName.split('/');
|
|
1056
|
+
return path_1.default.join(packagePath, 'node_modules', ...parts, 'package.json');
|
|
1057
|
+
}
|
|
1058
|
+
function getInstalledPackageDir(pkgName) {
|
|
1059
|
+
const parts = pkgName.split('/');
|
|
1060
|
+
return path_1.default.join(packagePath, 'node_modules', ...parts);
|
|
1061
|
+
}
|
|
1062
|
+
function findUp(startDir, relativePathToFind) {
|
|
1063
|
+
let dir = path_1.default.resolve(startDir);
|
|
1064
|
+
while (true) {
|
|
1065
|
+
const candidate = path_1.default.join(dir, relativePathToFind);
|
|
1066
|
+
if (fs_1.default.existsSync(candidate))
|
|
1067
|
+
return candidate;
|
|
1068
|
+
const parent = path_1.default.dirname(dir);
|
|
1069
|
+
if (parent === dir)
|
|
1070
|
+
return null;
|
|
1071
|
+
dir = parent;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
function ensurePeerDependencyAvailableForBuild(peerName) {
|
|
1075
|
+
var _a, _b;
|
|
1076
|
+
const installedPkgJson = getInstalledPackageJsonPath(peerName);
|
|
1077
|
+
if (fs_1.default.existsSync(installedPkgJson))
|
|
1078
|
+
return;
|
|
1079
|
+
const peerRange = (_a = packageJson.peerDependencies) === null || _a === void 0 ? void 0 : _a[peerName];
|
|
1080
|
+
const localMap = {
|
|
1081
|
+
'@openwebf/react-core-ui': path_1.default.join('packages', 'react-core-ui'),
|
|
1082
|
+
'@openwebf/vue-core-ui': path_1.default.join('packages', 'vue-core-ui'),
|
|
1083
|
+
};
|
|
1084
|
+
let installSpec = null;
|
|
1085
|
+
const localRel = localMap[peerName];
|
|
1086
|
+
if (localRel) {
|
|
1087
|
+
const localPath = findUp(process.cwd(), localRel);
|
|
1088
|
+
if (localPath) {
|
|
1089
|
+
if (!(0, peerDeps_1.isPackageTypesReady)(localPath)) {
|
|
1090
|
+
const localPkgJsonPath = path_1.default.join(localPath, 'package.json');
|
|
1091
|
+
if (fs_1.default.existsSync(localPkgJsonPath)) {
|
|
1092
|
+
const localPkgJson = (0, peerDeps_1.readJsonFile)(localPkgJsonPath);
|
|
1093
|
+
if ((_b = localPkgJson.scripts) === null || _b === void 0 ? void 0 : _b.build) {
|
|
1094
|
+
if (process.env.WEBF_CODEGEN_BUILD_LOCAL_PEERS !== '1') {
|
|
1095
|
+
console.warn(`\nā ļø Local ${peerName} found at ${localPath} but type declarations are missing; falling back to registry install.`);
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
console.log(`\nš§ Local ${peerName} found at ${localPath} but build artifacts are missing; building it for DTS...`);
|
|
1099
|
+
const buildLocalResult = (0, child_process_1.spawnSync)(NPM, ['run', 'build'], {
|
|
1100
|
+
cwd: localPath,
|
|
1101
|
+
stdio: 'inherit'
|
|
1102
|
+
});
|
|
1103
|
+
if (buildLocalResult.status === 0) {
|
|
1104
|
+
if ((0, peerDeps_1.isPackageTypesReady)(localPath)) {
|
|
1105
|
+
installSpec = localPath;
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
console.warn(`\nā ļø Built local ${peerName} but type declarations are still missing; falling back to registry install.`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
console.warn(`\nā ļø Failed to build local ${peerName}; falling back to registry install.`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
installSpec = localPath;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (!installSpec) {
|
|
1124
|
+
installSpec = peerRange ? `${peerName}@${peerRange}` : peerName;
|
|
1125
|
+
}
|
|
1126
|
+
console.log(`\nš¦ Installing peer dependency for build: ${peerName}...`);
|
|
1127
|
+
const installResult = (0, child_process_1.spawnSync)(NPM, ['install', '--no-save', installSpec], {
|
|
1128
|
+
cwd: packagePath,
|
|
1129
|
+
stdio: 'inherit'
|
|
1130
|
+
});
|
|
1131
|
+
if (installResult.status !== 0) {
|
|
1132
|
+
throw new Error(`Failed to install peer dependency for build: ${peerName}`);
|
|
1133
|
+
}
|
|
1134
|
+
const installedTypesFile = (0, peerDeps_1.getPackageTypesFileFromDir)(getInstalledPackageDir(peerName));
|
|
1135
|
+
if (installedTypesFile && !fs_1.default.existsSync(installedTypesFile)) {
|
|
1136
|
+
throw new Error(`Peer dependency ${peerName} was installed but type declarations were not found at ${installedTypesFile}`);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
780
1139
|
// Check if node_modules exists
|
|
781
1140
|
const nodeModulesPath = path_1.default.join(packagePath, 'node_modules');
|
|
782
1141
|
if (!fs_1.default.existsSync(nodeModulesPath)) {
|
|
@@ -797,6 +1156,13 @@ function buildPackage(packagePath) {
|
|
|
797
1156
|
}
|
|
798
1157
|
// Check if package has a build script
|
|
799
1158
|
if ((_a = packageJson.scripts) === null || _a === void 0 ? void 0 : _a.build) {
|
|
1159
|
+
// DTS build needs peer deps present locally to resolve types (even though they are not bundled).
|
|
1160
|
+
if ((_b = packageJson.peerDependencies) === null || _b === void 0 ? void 0 : _b['@openwebf/react-core-ui']) {
|
|
1161
|
+
ensurePeerDependencyAvailableForBuild('@openwebf/react-core-ui');
|
|
1162
|
+
}
|
|
1163
|
+
if ((_c = packageJson.peerDependencies) === null || _c === void 0 ? void 0 : _c['@openwebf/vue-core-ui']) {
|
|
1164
|
+
ensurePeerDependencyAvailableForBuild('@openwebf/vue-core-ui');
|
|
1165
|
+
}
|
|
800
1166
|
console.log(`\nBuilding ${packageName}@${packageVersion}...`);
|
|
801
1167
|
const buildResult = (0, child_process_1.spawnSync)(NPM, ['run', 'build'], {
|
|
802
1168
|
cwd: packagePath,
|
package/dist/generator.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.writeFileIfChanged = writeFileIfChanged;
|
|
15
16
|
exports.dartGen = dartGen;
|
|
16
17
|
exports.reactGen = reactGen;
|
|
17
18
|
exports.vueGen = vueGen;
|
|
@@ -101,7 +102,7 @@ function getTypeFiles(source, excludePatterns) {
|
|
|
101
102
|
try {
|
|
102
103
|
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
|
|
103
104
|
const ignore = excludePatterns ? [...defaultIgnore, ...excludePatterns] : defaultIgnore;
|
|
104
|
-
const files = glob_1.
|
|
105
|
+
const files = (0, glob_1.globSync)("**/*.d.ts", {
|
|
105
106
|
cwd: source,
|
|
106
107
|
ignore: ignore
|
|
107
108
|
});
|
|
@@ -377,11 +378,8 @@ function reactGen(_a) {
|
|
|
377
378
|
(0, logger_1.warn)(`Failed to merge into existing index.ts. Skipping modifications: ${indexFilePath}`);
|
|
378
379
|
}
|
|
379
380
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
(0, logger_1.info)(`Output directory: ${normalizedTarget}`);
|
|
383
|
-
(0, logger_1.info)('You can now import these components in your React project.');
|
|
384
|
-
// 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 {};`).
|
|
385
383
|
try {
|
|
386
384
|
const consts = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.ConstObject));
|
|
387
385
|
const enums = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.EnumObject));
|
|
@@ -394,38 +392,38 @@ function reactGen(_a) {
|
|
|
394
392
|
typeAliases.forEach(t => { if (!typeAliasMap.has(t.name))
|
|
395
393
|
typeAliasMap.set(t.name, t); });
|
|
396
394
|
const hasAny = constMap.size > 0 || enums.length > 0 || typeAliasMap.size > 0;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
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'));
|
|
426
422
|
}
|
|
427
|
-
|
|
428
|
-
|
|
423
|
+
catch (_b) { }
|
|
424
|
+
}
|
|
425
|
+
// Only re-export from index.ts when there are actual declarations to surface.
|
|
426
|
+
if (hasAny) {
|
|
429
427
|
try {
|
|
430
428
|
let current = '';
|
|
431
429
|
if (fs_1.default.existsSync(indexFilePath)) {
|
|
@@ -446,6 +444,10 @@ function reactGen(_a) {
|
|
|
446
444
|
catch (e) {
|
|
447
445
|
(0, logger_1.warn)('Failed to generate aggregated React types');
|
|
448
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.');
|
|
449
451
|
});
|
|
450
452
|
}
|
|
451
453
|
function vueGen(_a) {
|