@openwebf/webf 0.22.3 ā 0.22.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/IDLBlob.js +17 -0
- package/dist/analyzer.js +578 -0
- package/dist/analyzer_original.js +467 -0
- package/dist/commands.js +704 -0
- package/dist/dart.js +300 -0
- package/dist/declaration.js +63 -0
- package/dist/generator.js +466 -0
- package/dist/logger.js +103 -0
- package/dist/react.js +283 -0
- package/dist/utils.js +127 -0
- package/dist/vue.js +159 -0
- package/package.json +8 -1
- package/src/dart.ts +138 -7
- package/templates/class.dart.tpl +7 -2
- package/templates/react.package.json.tpl +1 -1
- package/CLAUDE.md +0 -206
- package/README-zhCN.md +0 -256
- package/TYPING_GUIDE.md +0 -208
- package/coverage/clover.xml +0 -1295
- package/coverage/coverage-final.json +0 -12
- package/coverage/lcov-report/IDLBlob.ts.html +0 -142
- package/coverage/lcov-report/analyzer.ts.html +0 -2158
- package/coverage/lcov-report/analyzer_original.ts.html +0 -1450
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/commands.ts.html +0 -700
- package/coverage/lcov-report/dart.ts.html +0 -490
- package/coverage/lcov-report/declaration.ts.html +0 -337
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/generator.ts.html +0 -1171
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/logger.ts.html +0 -424
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/react.ts.html +0 -619
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/utils.ts.html +0 -466
- package/coverage/lcov-report/vue.ts.html +0 -613
- package/coverage/lcov.info +0 -2149
- package/global.d.ts +0 -2
- package/jest.config.js +0 -24
- package/tsconfig.json +0 -30
package/dist/commands.js
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
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.generateCommand = generateCommand;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const os_1 = __importDefault(require("os"));
|
|
20
|
+
const generator_1 = require("./generator");
|
|
21
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
22
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
23
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
24
|
+
/**
|
|
25
|
+
* Sanitize a package name to comply with npm naming rules
|
|
26
|
+
* NPM package name rules:
|
|
27
|
+
* - Must be lowercase
|
|
28
|
+
* - Must be one word, no spaces
|
|
29
|
+
* - Can contain hyphens and underscores
|
|
30
|
+
* - Must start with a letter or number (or @ for scoped packages)
|
|
31
|
+
* - Cannot contain special characters except @ for scoped packages
|
|
32
|
+
* - Must be less than 214 characters
|
|
33
|
+
* - Cannot start with . or _
|
|
34
|
+
* - Cannot contain leading or trailing spaces
|
|
35
|
+
* - Cannot contain any non-URL-safe characters
|
|
36
|
+
*/
|
|
37
|
+
function sanitizePackageName(name) {
|
|
38
|
+
// Remove any leading/trailing whitespace
|
|
39
|
+
let sanitized = name.trim();
|
|
40
|
+
// Check if it's a scoped package
|
|
41
|
+
const isScoped = sanitized.startsWith('@');
|
|
42
|
+
let scope = '';
|
|
43
|
+
let packageName = sanitized;
|
|
44
|
+
if (isScoped) {
|
|
45
|
+
const parts = sanitized.split('/');
|
|
46
|
+
if (parts.length >= 2) {
|
|
47
|
+
scope = parts[0];
|
|
48
|
+
packageName = parts.slice(1).join('/');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Invalid scoped package, treat as regular
|
|
52
|
+
packageName = sanitized.substring(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Sanitize scope if present
|
|
56
|
+
if (scope) {
|
|
57
|
+
scope = scope.toLowerCase();
|
|
58
|
+
// Remove invalid characters from scope (keep only @ and alphanumeric/hyphen)
|
|
59
|
+
scope = scope.replace(/[^@a-z0-9-]/g, '');
|
|
60
|
+
if (scope === '@') {
|
|
61
|
+
scope = '@pkg'; // Default scope if only @ remains
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Sanitize package name part
|
|
65
|
+
packageName = packageName.toLowerCase();
|
|
66
|
+
packageName = packageName.replace(/\s+/g, '-');
|
|
67
|
+
packageName = packageName.replace(/[^a-z0-9\-_.]/g, '');
|
|
68
|
+
packageName = packageName.replace(/^[._]+/, '');
|
|
69
|
+
packageName = packageName.replace(/[._]+$/, '');
|
|
70
|
+
packageName = packageName.replace(/[-_.]{2,}/g, '-');
|
|
71
|
+
packageName = packageName.replace(/^-+/, '').replace(/-+$/, '');
|
|
72
|
+
// Ensure package name is not empty
|
|
73
|
+
if (!packageName) {
|
|
74
|
+
packageName = 'package';
|
|
75
|
+
}
|
|
76
|
+
// Ensure it starts with a letter or number
|
|
77
|
+
if (!/^[a-z0-9]/.test(packageName)) {
|
|
78
|
+
packageName = 'pkg-' + packageName;
|
|
79
|
+
}
|
|
80
|
+
// Combine scope and package name
|
|
81
|
+
let result = scope ? `${scope}/${packageName}` : packageName;
|
|
82
|
+
// Truncate to 214 characters (npm limit)
|
|
83
|
+
if (result.length > 214) {
|
|
84
|
+
if (scope) {
|
|
85
|
+
// Try to preserve scope
|
|
86
|
+
const maxPackageLength = 214 - scope.length - 1; // -1 for the /
|
|
87
|
+
packageName = packageName.substring(0, maxPackageLength);
|
|
88
|
+
packageName = packageName.replace(/[._-]+$/, '');
|
|
89
|
+
result = `${scope}/${packageName}`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
result = result.substring(0, 214);
|
|
93
|
+
result = result.replace(/[._-]+$/, '');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validate if a package name follows npm naming rules
|
|
100
|
+
*/
|
|
101
|
+
function isValidNpmPackageName(name) {
|
|
102
|
+
// Check basic rules
|
|
103
|
+
if (!name || name.length === 0 || name.length > 214)
|
|
104
|
+
return false;
|
|
105
|
+
if (name.trim() !== name)
|
|
106
|
+
return false;
|
|
107
|
+
// Check if it's a scoped package
|
|
108
|
+
if (name.startsWith('@')) {
|
|
109
|
+
const parts = name.split('/');
|
|
110
|
+
if (parts.length !== 2)
|
|
111
|
+
return false; // Scoped packages must have exactly one /
|
|
112
|
+
const scope = parts[0];
|
|
113
|
+
const packageName = parts[1];
|
|
114
|
+
// Validate scope
|
|
115
|
+
if (!/^@[a-z0-9][a-z0-9-]*$/.test(scope))
|
|
116
|
+
return false;
|
|
117
|
+
// Validate package name part
|
|
118
|
+
return isValidNpmPackageName(packageName);
|
|
119
|
+
}
|
|
120
|
+
// For non-scoped packages
|
|
121
|
+
if (name !== name.toLowerCase())
|
|
122
|
+
return false;
|
|
123
|
+
if (name.startsWith('.') || name.startsWith('_'))
|
|
124
|
+
return false;
|
|
125
|
+
// Check for valid characters (letters, numbers, hyphens, underscores, dots)
|
|
126
|
+
if (!/^[a-z0-9][a-z0-9\-_.]*$/.test(name))
|
|
127
|
+
return false;
|
|
128
|
+
// Check for URL-safe characters
|
|
129
|
+
try {
|
|
130
|
+
if (encodeURIComponent(name) !== name)
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
catch (_a) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
const platform = process.platform;
|
|
139
|
+
const NPM = platform == 'win32' ? 'npm.cmd' : 'npm';
|
|
140
|
+
const gloabalDts = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../global.d.ts'), 'utf-8');
|
|
141
|
+
const tsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/tsconfig.json.tpl'), 'utf-8');
|
|
142
|
+
const gitignore = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/gitignore.tpl'), 'utf-8');
|
|
143
|
+
const reactPackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.package.json.tpl'), 'utf-8');
|
|
144
|
+
const reactTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsconfig.json.tpl'), 'utf-8');
|
|
145
|
+
const reactTsUpConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.tsup.config.ts.tpl'), 'utf-8');
|
|
146
|
+
const reactIndexTpl = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/react.index.ts.tpl'), 'utf-8');
|
|
147
|
+
const vuePackageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/vue.package.json.tpl'), 'utf-8');
|
|
148
|
+
const vueTsConfig = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../templates/vue.tsconfig.json.tpl'), 'utf-8');
|
|
149
|
+
function readFlutterPackageMetadata(packagePath) {
|
|
150
|
+
try {
|
|
151
|
+
const pubspecPath = path_1.default.join(packagePath, 'pubspec.yaml');
|
|
152
|
+
if (!fs_1.default.existsSync(pubspecPath)) {
|
|
153
|
+
console.warn(`Warning: pubspec.yaml not found at ${pubspecPath}. Using default metadata.`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const pubspecContent = fs_1.default.readFileSync(pubspecPath, 'utf-8');
|
|
157
|
+
const pubspec = yaml_1.default.parse(pubspecContent);
|
|
158
|
+
// Validate required fields
|
|
159
|
+
if (!pubspec.name) {
|
|
160
|
+
console.warn(`Warning: Flutter package name not found in ${pubspecPath}. Using default name.`);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
name: pubspec.name || '',
|
|
164
|
+
version: pubspec.version || '0.0.1',
|
|
165
|
+
description: pubspec.description || ''
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.warn(`Warning: Could not read Flutter package metadata from ${packagePath}:`, error);
|
|
170
|
+
console.warn('Using default metadata. Ensure pubspec.yaml exists and is valid YAML.');
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function validateTypeScriptEnvironment(projectPath) {
|
|
175
|
+
const errors = [];
|
|
176
|
+
// Check for TypeScript configuration
|
|
177
|
+
const tsConfigPath = path_1.default.join(projectPath, 'tsconfig.json');
|
|
178
|
+
if (!fs_1.default.existsSync(tsConfigPath)) {
|
|
179
|
+
errors.push('Missing tsconfig.json - TypeScript configuration is required for type definitions');
|
|
180
|
+
}
|
|
181
|
+
// Check for .d.ts files - this is critical
|
|
182
|
+
const libPath = path_1.default.join(projectPath, 'lib');
|
|
183
|
+
let hasDtsFiles = false;
|
|
184
|
+
if (fs_1.default.existsSync(libPath)) {
|
|
185
|
+
// Check in lib directory
|
|
186
|
+
hasDtsFiles = fs_1.default.readdirSync(libPath).some(file => file.endsWith('.d.ts') ||
|
|
187
|
+
(fs_1.default.statSync(path_1.default.join(libPath, file)).isDirectory() &&
|
|
188
|
+
fs_1.default.readdirSync(path_1.default.join(libPath, file)).some(f => f.endsWith('.d.ts'))));
|
|
189
|
+
}
|
|
190
|
+
// Also check in root directory
|
|
191
|
+
if (!hasDtsFiles) {
|
|
192
|
+
hasDtsFiles = fs_1.default.readdirSync(projectPath).some(file => file.endsWith('.d.ts') ||
|
|
193
|
+
(fs_1.default.statSync(path_1.default.join(projectPath, file)).isDirectory() &&
|
|
194
|
+
file !== 'node_modules' &&
|
|
195
|
+
fs_1.default.existsSync(path_1.default.join(projectPath, file, 'index.d.ts'))));
|
|
196
|
+
}
|
|
197
|
+
if (!hasDtsFiles) {
|
|
198
|
+
errors.push('No TypeScript definition files (.d.ts) found in the project - Please create .d.ts files for your components');
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
isValid: errors.length === 0,
|
|
202
|
+
errors
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function createCommand(target, options) {
|
|
206
|
+
const { framework, metadata } = options;
|
|
207
|
+
// Ensure package name is always valid
|
|
208
|
+
const packageName = isValidNpmPackageName(options.packageName)
|
|
209
|
+
? options.packageName
|
|
210
|
+
: sanitizePackageName(options.packageName);
|
|
211
|
+
if (!fs_1.default.existsSync(target)) {
|
|
212
|
+
fs_1.default.mkdirSync(target, { recursive: true });
|
|
213
|
+
}
|
|
214
|
+
if (framework === 'react') {
|
|
215
|
+
const packageJsonPath = path_1.default.join(target, 'package.json');
|
|
216
|
+
const packageJsonContent = lodash_1.default.template(reactPackageJson)({
|
|
217
|
+
packageName,
|
|
218
|
+
version: (metadata === null || metadata === void 0 ? void 0 : metadata.version) || '0.0.1',
|
|
219
|
+
description: (metadata === null || metadata === void 0 ? void 0 : metadata.description) || ''
|
|
220
|
+
});
|
|
221
|
+
writeFileIfChanged(packageJsonPath, packageJsonContent);
|
|
222
|
+
const tsConfigPath = path_1.default.join(target, 'tsconfig.json');
|
|
223
|
+
const tsConfigContent = lodash_1.default.template(reactTsConfig)({});
|
|
224
|
+
writeFileIfChanged(tsConfigPath, tsConfigContent);
|
|
225
|
+
const tsupConfigPath = path_1.default.join(target, 'tsup.config.ts');
|
|
226
|
+
const tsupConfigContent = lodash_1.default.template(reactTsUpConfig)({});
|
|
227
|
+
writeFileIfChanged(tsupConfigPath, tsupConfigContent);
|
|
228
|
+
const gitignorePath = path_1.default.join(target, '.gitignore');
|
|
229
|
+
const gitignoreContent = lodash_1.default.template(gitignore)({});
|
|
230
|
+
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
231
|
+
const srcDir = path_1.default.join(target, 'src');
|
|
232
|
+
if (!fs_1.default.existsSync(srcDir)) {
|
|
233
|
+
fs_1.default.mkdirSync(srcDir, { recursive: true });
|
|
234
|
+
}
|
|
235
|
+
const indexFilePath = path_1.default.join(srcDir, 'index.ts');
|
|
236
|
+
const indexContent = lodash_1.default.template(reactIndexTpl)({
|
|
237
|
+
components: [],
|
|
238
|
+
});
|
|
239
|
+
writeFileIfChanged(indexFilePath, indexContent);
|
|
240
|
+
(0, child_process_1.spawnSync)(NPM, ['install', '--omit=peer'], {
|
|
241
|
+
cwd: target,
|
|
242
|
+
stdio: 'inherit'
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else if (framework === 'vue') {
|
|
246
|
+
const packageJsonPath = path_1.default.join(target, 'package.json');
|
|
247
|
+
const packageJsonContent = lodash_1.default.template(vuePackageJson)({
|
|
248
|
+
packageName,
|
|
249
|
+
version: (metadata === null || metadata === void 0 ? void 0 : metadata.version) || '0.0.1',
|
|
250
|
+
description: (metadata === null || metadata === void 0 ? void 0 : metadata.description) || ''
|
|
251
|
+
});
|
|
252
|
+
writeFileIfChanged(packageJsonPath, packageJsonContent);
|
|
253
|
+
const tsConfigPath = path_1.default.join(target, 'tsconfig.json');
|
|
254
|
+
const tsConfigContent = lodash_1.default.template(vueTsConfig)({});
|
|
255
|
+
writeFileIfChanged(tsConfigPath, tsConfigContent);
|
|
256
|
+
const gitignorePath = path_1.default.join(target, '.gitignore');
|
|
257
|
+
const gitignoreContent = lodash_1.default.template(gitignore)({});
|
|
258
|
+
writeFileIfChanged(gitignorePath, gitignoreContent);
|
|
259
|
+
(0, child_process_1.spawnSync)(NPM, ['install', '@openwebf/webf-enterprise-typings'], {
|
|
260
|
+
cwd: target,
|
|
261
|
+
stdio: 'inherit'
|
|
262
|
+
});
|
|
263
|
+
(0, child_process_1.spawnSync)(NPM, ['install', '@types/vue', '-D'], {
|
|
264
|
+
cwd: target,
|
|
265
|
+
stdio: 'inherit'
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
console.log(`WebF ${framework} package created at: ${target}`);
|
|
269
|
+
}
|
|
270
|
+
function generateCommand(distPath, options) {
|
|
271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
272
|
+
var _a, _b, _c, _d;
|
|
273
|
+
// If distPath is not provided or is '.', create a temporary directory
|
|
274
|
+
let resolvedDistPath;
|
|
275
|
+
let isTempDir = false;
|
|
276
|
+
if (!distPath || distPath === '.') {
|
|
277
|
+
// Create a temporary directory for the generated package
|
|
278
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'webf-typings-'));
|
|
279
|
+
resolvedDistPath = tempDir;
|
|
280
|
+
isTempDir = true;
|
|
281
|
+
console.log(`\nUsing temporary directory: ${tempDir}`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
resolvedDistPath = path_1.default.resolve(distPath);
|
|
285
|
+
}
|
|
286
|
+
// First, check if we're in a Flutter package directory when flutter-package-src is not provided
|
|
287
|
+
if (!options.flutterPackageSrc) {
|
|
288
|
+
// Check if current directory or parent directories contain pubspec.yaml
|
|
289
|
+
let currentDir = process.cwd();
|
|
290
|
+
let foundPubspec = false;
|
|
291
|
+
let pubspecDir = '';
|
|
292
|
+
// Search up to 3 levels up for pubspec.yaml
|
|
293
|
+
for (let i = 0; i < 3; i++) {
|
|
294
|
+
const pubspecPath = path_1.default.join(currentDir, 'pubspec.yaml');
|
|
295
|
+
if (fs_1.default.existsSync(pubspecPath)) {
|
|
296
|
+
foundPubspec = true;
|
|
297
|
+
pubspecDir = currentDir;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
301
|
+
if (parentDir === currentDir)
|
|
302
|
+
break; // Reached root
|
|
303
|
+
currentDir = parentDir;
|
|
304
|
+
}
|
|
305
|
+
if (foundPubspec) {
|
|
306
|
+
// Use the directory containing pubspec.yaml as the flutter package source
|
|
307
|
+
options.flutterPackageSrc = pubspecDir;
|
|
308
|
+
console.log(`\nDetected Flutter package at: ${pubspecDir}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Check if the directory exists and has required files
|
|
312
|
+
const packageJsonPath = path_1.default.join(resolvedDistPath, 'package.json');
|
|
313
|
+
const globalDtsPath = path_1.default.join(resolvedDistPath, 'global.d.ts');
|
|
314
|
+
const tsConfigPath = path_1.default.join(resolvedDistPath, 'tsconfig.json');
|
|
315
|
+
const hasPackageJson = fs_1.default.existsSync(packageJsonPath);
|
|
316
|
+
const hasGlobalDts = fs_1.default.existsSync(globalDtsPath);
|
|
317
|
+
const hasTsConfig = fs_1.default.existsSync(tsConfigPath);
|
|
318
|
+
// Determine if we need to create a new project
|
|
319
|
+
const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
|
|
320
|
+
let framework = options.framework;
|
|
321
|
+
let packageName = options.packageName;
|
|
322
|
+
// Validate and sanitize package name if provided
|
|
323
|
+
if (packageName && !isValidNpmPackageName(packageName)) {
|
|
324
|
+
console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
|
|
325
|
+
const sanitized = sanitizePackageName(packageName);
|
|
326
|
+
console.log(`Using sanitized name: "${sanitized}"`);
|
|
327
|
+
packageName = sanitized;
|
|
328
|
+
}
|
|
329
|
+
if (needsProjectCreation) {
|
|
330
|
+
// If project needs creation but options are missing, prompt for them
|
|
331
|
+
if (!framework) {
|
|
332
|
+
const frameworkAnswer = yield inquirer_1.default.prompt([{
|
|
333
|
+
type: 'list',
|
|
334
|
+
name: 'framework',
|
|
335
|
+
message: 'Which framework would you like to use?',
|
|
336
|
+
choices: ['react', 'vue']
|
|
337
|
+
}]);
|
|
338
|
+
framework = frameworkAnswer.framework;
|
|
339
|
+
}
|
|
340
|
+
// Try to read Flutter package metadata if flutterPackageSrc is provided
|
|
341
|
+
let metadata = null;
|
|
342
|
+
if (options.flutterPackageSrc) {
|
|
343
|
+
metadata = readFlutterPackageMetadata(options.flutterPackageSrc);
|
|
344
|
+
}
|
|
345
|
+
if (!packageName) {
|
|
346
|
+
// Use Flutter package name as default if available, sanitized for npm
|
|
347
|
+
const rawDefaultName = (metadata === null || metadata === void 0 ? void 0 : metadata.name) || path_1.default.basename(resolvedDistPath);
|
|
348
|
+
const defaultPackageName = sanitizePackageName(rawDefaultName);
|
|
349
|
+
const packageNameAnswer = yield inquirer_1.default.prompt([{
|
|
350
|
+
type: 'input',
|
|
351
|
+
name: 'packageName',
|
|
352
|
+
message: 'What is your package name?',
|
|
353
|
+
default: defaultPackageName,
|
|
354
|
+
validate: (input) => {
|
|
355
|
+
if (!input || input.trim() === '') {
|
|
356
|
+
return 'Package name is required';
|
|
357
|
+
}
|
|
358
|
+
// Check if it's valid as-is
|
|
359
|
+
if (isValidNpmPackageName(input)) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
// If not valid, show what it would be sanitized to
|
|
363
|
+
const sanitized = sanitizePackageName(input);
|
|
364
|
+
return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
|
|
365
|
+
}
|
|
366
|
+
}]);
|
|
367
|
+
packageName = packageNameAnswer.packageName;
|
|
368
|
+
}
|
|
369
|
+
console.log(`\nCreating new ${framework} project in ${resolvedDistPath}...`);
|
|
370
|
+
createCommand(resolvedDistPath, {
|
|
371
|
+
framework: framework,
|
|
372
|
+
packageName: packageName,
|
|
373
|
+
metadata: metadata || undefined
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
// Validate existing project structure
|
|
378
|
+
if (hasPackageJson) {
|
|
379
|
+
try {
|
|
380
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
381
|
+
// Detect framework from existing package.json
|
|
382
|
+
if (!framework) {
|
|
383
|
+
if (((_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a.react) || ((_b = packageJson.devDependencies) === null || _b === void 0 ? void 0 : _b.react)) {
|
|
384
|
+
framework = 'react';
|
|
385
|
+
}
|
|
386
|
+
else if (((_c = packageJson.dependencies) === null || _c === void 0 ? void 0 : _c.vue) || ((_d = packageJson.devDependencies) === null || _d === void 0 ? void 0 : _d.vue)) {
|
|
387
|
+
framework = 'vue';
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// If can't detect, prompt for it
|
|
391
|
+
const frameworkAnswer = yield inquirer_1.default.prompt([{
|
|
392
|
+
type: 'list',
|
|
393
|
+
name: 'framework',
|
|
394
|
+
message: 'Which framework are you using?',
|
|
395
|
+
choices: ['react', 'vue']
|
|
396
|
+
}]);
|
|
397
|
+
framework = frameworkAnswer.framework;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
|
|
401
|
+
}
|
|
402
|
+
catch (e) {
|
|
403
|
+
console.error('Error reading package.json:', e);
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Now proceed with code generation if flutter package source is provided
|
|
409
|
+
if (!options.flutterPackageSrc) {
|
|
410
|
+
console.log('\nProject is ready for code generation.');
|
|
411
|
+
console.log('To generate code, run:');
|
|
412
|
+
const displayPath = isTempDir ? '<output-dir>' : distPath;
|
|
413
|
+
console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --framework=${framework}`);
|
|
414
|
+
if (isTempDir) {
|
|
415
|
+
// Clean up temporary directory if we're not using it
|
|
416
|
+
fs_1.default.rmSync(resolvedDistPath, { recursive: true, force: true });
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
// Validate TypeScript environment in the Flutter package
|
|
421
|
+
console.log(`\nValidating TypeScript environment in ${options.flutterPackageSrc}...`);
|
|
422
|
+
const validation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
423
|
+
if (!validation.isValid) {
|
|
424
|
+
// Check specifically for missing tsconfig.json
|
|
425
|
+
const tsConfigPath = path_1.default.join(options.flutterPackageSrc, 'tsconfig.json');
|
|
426
|
+
if (!fs_1.default.existsSync(tsConfigPath)) {
|
|
427
|
+
const createTsConfigAnswer = yield inquirer_1.default.prompt([{
|
|
428
|
+
type: 'confirm',
|
|
429
|
+
name: 'createTsConfig',
|
|
430
|
+
message: 'No tsconfig.json found. Would you like me to create one for you?',
|
|
431
|
+
default: true
|
|
432
|
+
}]);
|
|
433
|
+
if (createTsConfigAnswer.createTsConfig) {
|
|
434
|
+
// Create a default tsconfig.json
|
|
435
|
+
const defaultTsConfig = {
|
|
436
|
+
compilerOptions: {
|
|
437
|
+
target: 'ES2020',
|
|
438
|
+
module: 'commonjs',
|
|
439
|
+
lib: ['ES2020'],
|
|
440
|
+
declaration: true,
|
|
441
|
+
strict: true,
|
|
442
|
+
esModuleInterop: true,
|
|
443
|
+
skipLibCheck: true,
|
|
444
|
+
forceConsistentCasingInFileNames: true,
|
|
445
|
+
resolveJsonModule: true,
|
|
446
|
+
moduleResolution: 'node'
|
|
447
|
+
},
|
|
448
|
+
include: ['lib/**/*.d.ts', '**/*.d.ts'],
|
|
449
|
+
exclude: ['node_modules', 'dist', 'build']
|
|
450
|
+
};
|
|
451
|
+
fs_1.default.writeFileSync(tsConfigPath, JSON.stringify(defaultTsConfig, null, 2), 'utf-8');
|
|
452
|
+
console.log('ā
Created tsconfig.json');
|
|
453
|
+
// Re-validate after creating tsconfig
|
|
454
|
+
const newValidation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
455
|
+
if (!newValidation.isValid) {
|
|
456
|
+
console.error('\nā ļø Additional setup required:');
|
|
457
|
+
newValidation.errors.forEach(error => console.error(` - ${error}`));
|
|
458
|
+
console.error('\nPlease fix the above issues and run the command again.');
|
|
459
|
+
process.exit(1);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
console.error('\nā TypeScript configuration is required for code generation.');
|
|
464
|
+
console.error('Please create a tsconfig.json file manually and run the command again.');
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
// Show all validation errors
|
|
470
|
+
console.error('\nā TypeScript environment validation failed:');
|
|
471
|
+
validation.errors.forEach(error => console.error(` - ${error}`));
|
|
472
|
+
console.error('\nPlease fix the above issues before generating code.');
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const command = `webf codegen --flutter-package-src=${options.flutterPackageSrc} --framework=${framework} <distPath>`;
|
|
477
|
+
// Auto-initialize typings in the output directory if needed
|
|
478
|
+
ensureInitialized(resolvedDistPath);
|
|
479
|
+
console.log(`\nGenerating ${framework} code from ${options.flutterPackageSrc}...`);
|
|
480
|
+
yield (0, generator_1.dartGen)({
|
|
481
|
+
source: options.flutterPackageSrc,
|
|
482
|
+
target: options.flutterPackageSrc,
|
|
483
|
+
command,
|
|
484
|
+
exclude: options.exclude,
|
|
485
|
+
});
|
|
486
|
+
if (framework === 'react') {
|
|
487
|
+
// Get the package name from package.json if it exists
|
|
488
|
+
let reactPackageName;
|
|
489
|
+
try {
|
|
490
|
+
const packageJsonPath = path_1.default.join(resolvedDistPath, 'package.json');
|
|
491
|
+
if (fs_1.default.existsSync(packageJsonPath)) {
|
|
492
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
493
|
+
reactPackageName = packageJson.name;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch (e) {
|
|
497
|
+
// Ignore errors
|
|
498
|
+
}
|
|
499
|
+
yield (0, generator_1.reactGen)({
|
|
500
|
+
source: options.flutterPackageSrc,
|
|
501
|
+
target: resolvedDistPath,
|
|
502
|
+
command,
|
|
503
|
+
exclude: options.exclude,
|
|
504
|
+
packageName: reactPackageName,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else if (framework === 'vue') {
|
|
508
|
+
yield (0, generator_1.vueGen)({
|
|
509
|
+
source: options.flutterPackageSrc,
|
|
510
|
+
target: resolvedDistPath,
|
|
511
|
+
command,
|
|
512
|
+
exclude: options.exclude,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
console.log('\nCode generation completed successfully!');
|
|
516
|
+
// Automatically build the generated package
|
|
517
|
+
if (framework) {
|
|
518
|
+
try {
|
|
519
|
+
yield buildPackage(resolvedDistPath);
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
console.error('\nWarning: Build failed:', error);
|
|
523
|
+
// Don't exit here since generation was successful
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// Handle npm publishing if requested via command line option
|
|
527
|
+
if (options.publishToNpm && framework) {
|
|
528
|
+
try {
|
|
529
|
+
yield buildAndPublishPackage(resolvedDistPath, options.npmRegistry);
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
console.error('\nError during npm publish:', error);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (framework && !options.publishToNpm) {
|
|
537
|
+
// If not publishing via command line option, ask the user
|
|
538
|
+
const publishAnswer = yield inquirer_1.default.prompt([{
|
|
539
|
+
type: 'confirm',
|
|
540
|
+
name: 'publish',
|
|
541
|
+
message: 'Would you like to publish this package to npm?',
|
|
542
|
+
default: false
|
|
543
|
+
}]);
|
|
544
|
+
if (publishAnswer.publish) {
|
|
545
|
+
// Ask for registry
|
|
546
|
+
const registryAnswer = yield inquirer_1.default.prompt([{
|
|
547
|
+
type: 'input',
|
|
548
|
+
name: 'registry',
|
|
549
|
+
message: 'NPM registry URL (leave empty for default npm registry):',
|
|
550
|
+
default: '',
|
|
551
|
+
validate: (input) => {
|
|
552
|
+
if (!input)
|
|
553
|
+
return true; // Empty is valid (use default)
|
|
554
|
+
try {
|
|
555
|
+
new URL(input); // Validate URL format
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
catch (_a) {
|
|
559
|
+
return 'Please enter a valid URL';
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}]);
|
|
563
|
+
try {
|
|
564
|
+
yield buildAndPublishPackage(resolvedDistPath, registryAnswer.registry || undefined);
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
console.error('\nError during npm publish:', error);
|
|
568
|
+
// Don't exit here since generation was successful
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// If using a temporary directory, remind the user where the files are
|
|
573
|
+
if (isTempDir) {
|
|
574
|
+
console.log(`\nš Generated files are in: ${resolvedDistPath}`);
|
|
575
|
+
console.log('š” To use these files, copy them to your desired location or publish to npm.');
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
function writeFileIfChanged(filePath, content) {
|
|
580
|
+
if (fs_1.default.existsSync(filePath)) {
|
|
581
|
+
const oldContent = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
582
|
+
if (oldContent === content) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
587
|
+
}
|
|
588
|
+
function ensureInitialized(targetPath) {
|
|
589
|
+
const globalDtsPath = path_1.default.join(targetPath, 'global.d.ts');
|
|
590
|
+
const tsConfigPath = path_1.default.join(targetPath, 'tsconfig.json');
|
|
591
|
+
// Check if initialization files already exist
|
|
592
|
+
const needsInit = !fs_1.default.existsSync(globalDtsPath) || !fs_1.default.existsSync(tsConfigPath);
|
|
593
|
+
if (needsInit) {
|
|
594
|
+
console.log('Initializing WebF typings...');
|
|
595
|
+
fs_1.default.mkdirSync(targetPath, { recursive: true });
|
|
596
|
+
if (!fs_1.default.existsSync(globalDtsPath)) {
|
|
597
|
+
fs_1.default.writeFileSync(globalDtsPath, gloabalDts, 'utf-8');
|
|
598
|
+
console.log('Created global.d.ts');
|
|
599
|
+
}
|
|
600
|
+
if (!fs_1.default.existsSync(tsConfigPath)) {
|
|
601
|
+
fs_1.default.writeFileSync(tsConfigPath, tsConfig, 'utf-8');
|
|
602
|
+
console.log('Created tsconfig.json');
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function buildPackage(packagePath) {
|
|
607
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
608
|
+
var _a;
|
|
609
|
+
const packageJsonPath = path_1.default.join(packagePath, 'package.json');
|
|
610
|
+
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
611
|
+
// Skip the error in test environment to avoid console warnings
|
|
612
|
+
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
throw new Error(`No package.json found in ${packagePath}`);
|
|
616
|
+
}
|
|
617
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
618
|
+
const packageName = packageJson.name;
|
|
619
|
+
const packageVersion = packageJson.version;
|
|
620
|
+
// Check if node_modules exists
|
|
621
|
+
const nodeModulesPath = path_1.default.join(packagePath, 'node_modules');
|
|
622
|
+
if (!fs_1.default.existsSync(nodeModulesPath)) {
|
|
623
|
+
console.log(`\nš¦ Installing dependencies for ${packageName}...`);
|
|
624
|
+
// Check if yarn.lock exists to determine package manager
|
|
625
|
+
const yarnLockPath = path_1.default.join(packagePath, 'yarn.lock');
|
|
626
|
+
const useYarn = fs_1.default.existsSync(yarnLockPath);
|
|
627
|
+
const installCommand = useYarn ? 'yarn' : NPM;
|
|
628
|
+
const installArgs = useYarn ? [] : ['install'];
|
|
629
|
+
const installResult = (0, child_process_1.spawnSync)(installCommand, installArgs, {
|
|
630
|
+
cwd: packagePath,
|
|
631
|
+
stdio: 'inherit'
|
|
632
|
+
});
|
|
633
|
+
if (installResult.status !== 0) {
|
|
634
|
+
throw new Error('Failed to install dependencies');
|
|
635
|
+
}
|
|
636
|
+
console.log('ā
Dependencies installed successfully!');
|
|
637
|
+
}
|
|
638
|
+
// Check if package has a build script
|
|
639
|
+
if ((_a = packageJson.scripts) === null || _a === void 0 ? void 0 : _a.build) {
|
|
640
|
+
console.log(`\nBuilding ${packageName}@${packageVersion}...`);
|
|
641
|
+
const buildResult = (0, child_process_1.spawnSync)(NPM, ['run', 'build'], {
|
|
642
|
+
cwd: packagePath,
|
|
643
|
+
stdio: 'inherit'
|
|
644
|
+
});
|
|
645
|
+
if (buildResult.status !== 0) {
|
|
646
|
+
throw new Error('Build failed');
|
|
647
|
+
}
|
|
648
|
+
console.log('ā
Build completed successfully!');
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
console.log(`\nNo build script found for ${packageName}@${packageVersion}`);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
function buildAndPublishPackage(packagePath, registry) {
|
|
656
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
657
|
+
const packageJsonPath = path_1.default.join(packagePath, 'package.json');
|
|
658
|
+
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
659
|
+
throw new Error(`No package.json found in ${packagePath}`);
|
|
660
|
+
}
|
|
661
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
662
|
+
const packageName = packageJson.name;
|
|
663
|
+
const packageVersion = packageJson.version;
|
|
664
|
+
// First, ensure dependencies are installed and build the package
|
|
665
|
+
yield buildPackage(packagePath);
|
|
666
|
+
// Set registry if provided
|
|
667
|
+
if (registry) {
|
|
668
|
+
console.log(`\nUsing npm registry: ${registry}`);
|
|
669
|
+
const setRegistryResult = (0, child_process_1.spawnSync)(NPM, ['config', 'set', 'registry', registry], {
|
|
670
|
+
cwd: packagePath,
|
|
671
|
+
stdio: 'inherit'
|
|
672
|
+
});
|
|
673
|
+
if (setRegistryResult.status !== 0) {
|
|
674
|
+
throw new Error('Failed to set npm registry');
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Check if user is logged in to npm
|
|
678
|
+
const whoamiResult = (0, child_process_1.spawnSync)(NPM, ['whoami'], {
|
|
679
|
+
cwd: packagePath,
|
|
680
|
+
encoding: 'utf-8'
|
|
681
|
+
});
|
|
682
|
+
if (whoamiResult.status !== 0) {
|
|
683
|
+
console.error('\nError: You must be logged in to npm to publish packages.');
|
|
684
|
+
console.error('Please run "npm login" first.');
|
|
685
|
+
throw new Error('Not logged in to npm');
|
|
686
|
+
}
|
|
687
|
+
console.log(`\nPublishing ${packageName}@${packageVersion} to npm...`);
|
|
688
|
+
// Publish the package
|
|
689
|
+
const publishResult = (0, child_process_1.spawnSync)(NPM, ['publish'], {
|
|
690
|
+
cwd: packagePath,
|
|
691
|
+
stdio: 'inherit'
|
|
692
|
+
});
|
|
693
|
+
if (publishResult.status !== 0) {
|
|
694
|
+
throw new Error('Publish failed');
|
|
695
|
+
}
|
|
696
|
+
console.log(`\nā
Successfully published ${packageName}@${packageVersion}`);
|
|
697
|
+
// Reset registry to default if it was changed
|
|
698
|
+
if (registry) {
|
|
699
|
+
(0, child_process_1.spawnSync)(NPM, ['config', 'delete', 'registry'], {
|
|
700
|
+
cwd: packagePath
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|