@openwebf/webf 0.23.0 ā 0.23.7
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 +5 -1
- package/TYPING_GUIDE.md +17 -1
- package/bin/webf.js +1 -0
- package/dist/analyzer.js +135 -17
- package/dist/commands.js +251 -99
- package/dist/constants.js +242 -0
- package/dist/dart.js +91 -25
- package/dist/declaration.js +14 -1
- package/dist/generator.js +80 -12
- package/dist/react.js +281 -23
- package/dist/vue.js +114 -11
- package/package.json +1 -1
- package/src/IDLBlob.ts +2 -2
- package/src/analyzer.ts +142 -28
- package/src/commands.ts +363 -197
- package/src/dart.ts +95 -20
- package/src/declaration.ts +16 -0
- package/src/generator.ts +81 -13
- package/src/react.ts +300 -28
- package/src/vue.ts +131 -14
- package/templates/class.dart.tpl +1 -1
- package/templates/react.package.json.tpl +1 -0
- package/templates/vue.components.d.ts.tpl +9 -4
- package/test/commands.test.ts +82 -2
- package/test/dart-nullable-props.test.ts +58 -0
- package/test/react-consts.test.ts +30 -0
- package/test/react-vue-nullable-props.test.ts +66 -0
- package/test/react.test.ts +46 -4
- package/test/vue.test.ts +34 -2
package/src/commands.ts
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import { dartGen, reactGen, vueGen } from './generator';
|
|
6
|
+
import { glob } from 'glob';
|
|
6
7
|
import _ from 'lodash';
|
|
7
8
|
import inquirer from 'inquirer';
|
|
8
9
|
import yaml from 'yaml';
|
|
@@ -14,6 +15,7 @@ interface GenerateOptions {
|
|
|
14
15
|
publishToNpm?: boolean;
|
|
15
16
|
npmRegistry?: string;
|
|
16
17
|
exclude?: string[];
|
|
18
|
+
dartOnly?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
interface FlutterPackageMetadata {
|
|
@@ -38,12 +40,12 @@ interface FlutterPackageMetadata {
|
|
|
38
40
|
function sanitizePackageName(name: string): string {
|
|
39
41
|
// Remove any leading/trailing whitespace
|
|
40
42
|
let sanitized = name.trim();
|
|
41
|
-
|
|
43
|
+
|
|
42
44
|
// Check if it's a scoped package
|
|
43
45
|
const isScoped = sanitized.startsWith('@');
|
|
44
46
|
let scope = '';
|
|
45
47
|
let packageName = sanitized;
|
|
46
|
-
|
|
48
|
+
|
|
47
49
|
if (isScoped) {
|
|
48
50
|
const parts = sanitized.split('/');
|
|
49
51
|
if (parts.length >= 2) {
|
|
@@ -54,7 +56,7 @@ function sanitizePackageName(name: string): string {
|
|
|
54
56
|
packageName = sanitized.substring(1);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
// Sanitize scope if present
|
|
59
61
|
if (scope) {
|
|
60
62
|
scope = scope.toLowerCase();
|
|
@@ -64,7 +66,7 @@ function sanitizePackageName(name: string): string {
|
|
|
64
66
|
scope = '@pkg'; // Default scope if only @ remains
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
// Sanitize package name part
|
|
69
71
|
packageName = packageName.toLowerCase();
|
|
70
72
|
packageName = packageName.replace(/\s+/g, '-');
|
|
@@ -73,20 +75,20 @@ function sanitizePackageName(name: string): string {
|
|
|
73
75
|
packageName = packageName.replace(/[._]+$/, '');
|
|
74
76
|
packageName = packageName.replace(/[-_.]{2,}/g, '-');
|
|
75
77
|
packageName = packageName.replace(/^-+/, '').replace(/-+$/, '');
|
|
76
|
-
|
|
78
|
+
|
|
77
79
|
// Ensure package name is not empty
|
|
78
80
|
if (!packageName) {
|
|
79
81
|
packageName = 'package';
|
|
80
82
|
}
|
|
81
|
-
|
|
83
|
+
|
|
82
84
|
// Ensure it starts with a letter or number
|
|
83
85
|
if (!/^[a-z0-9]/.test(packageName)) {
|
|
84
86
|
packageName = 'pkg-' + packageName;
|
|
85
87
|
}
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
// Combine scope and package name
|
|
88
90
|
let result = scope ? `${scope}/${packageName}` : packageName;
|
|
89
|
-
|
|
91
|
+
|
|
90
92
|
// Truncate to 214 characters (npm limit)
|
|
91
93
|
if (result.length > 214) {
|
|
92
94
|
if (scope) {
|
|
@@ -100,7 +102,7 @@ function sanitizePackageName(name: string): string {
|
|
|
100
102
|
result = result.replace(/[._-]+$/, '');
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
return result;
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -111,36 +113,36 @@ function isValidNpmPackageName(name: string): boolean {
|
|
|
111
113
|
// Check basic rules
|
|
112
114
|
if (!name || name.length === 0 || name.length > 214) return false;
|
|
113
115
|
if (name.trim() !== name) return false;
|
|
114
|
-
|
|
116
|
+
|
|
115
117
|
// Check if it's a scoped package
|
|
116
118
|
if (name.startsWith('@')) {
|
|
117
119
|
const parts = name.split('/');
|
|
118
120
|
if (parts.length !== 2) return false; // Scoped packages must have exactly one /
|
|
119
|
-
|
|
121
|
+
|
|
120
122
|
const scope = parts[0];
|
|
121
123
|
const packageName = parts[1];
|
|
122
|
-
|
|
124
|
+
|
|
123
125
|
// Validate scope
|
|
124
126
|
if (!/^@[a-z0-9][a-z0-9-]*$/.test(scope)) return false;
|
|
125
|
-
|
|
127
|
+
|
|
126
128
|
// Validate package name part
|
|
127
129
|
return isValidNpmPackageName(packageName);
|
|
128
130
|
}
|
|
129
|
-
|
|
131
|
+
|
|
130
132
|
// For non-scoped packages
|
|
131
133
|
if (name !== name.toLowerCase()) return false;
|
|
132
134
|
if (name.startsWith('.') || name.startsWith('_')) return false;
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
// Check for valid characters (letters, numbers, hyphens, underscores, dots)
|
|
135
137
|
if (!/^[a-z0-9][a-z0-9\-_.]*$/.test(name)) return false;
|
|
136
|
-
|
|
138
|
+
|
|
137
139
|
// Check for URL-safe characters
|
|
138
140
|
try {
|
|
139
141
|
if (encodeURIComponent(name) !== name) return false;
|
|
140
142
|
} catch {
|
|
141
143
|
return false;
|
|
142
144
|
}
|
|
143
|
-
|
|
145
|
+
|
|
144
146
|
return true;
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -199,15 +201,15 @@ function readFlutterPackageMetadata(packagePath: string): FlutterPackageMetadata
|
|
|
199
201
|
console.warn(`Warning: pubspec.yaml not found at ${pubspecPath}. Using default metadata.`);
|
|
200
202
|
return null;
|
|
201
203
|
}
|
|
202
|
-
|
|
204
|
+
|
|
203
205
|
const pubspecContent = fs.readFileSync(pubspecPath, 'utf-8');
|
|
204
206
|
const pubspec = yaml.parse(pubspecContent);
|
|
205
|
-
|
|
207
|
+
|
|
206
208
|
// Validate required fields
|
|
207
209
|
if (!pubspec.name) {
|
|
208
210
|
console.warn(`Warning: Flutter package name not found in ${pubspecPath}. Using default name.`);
|
|
209
211
|
}
|
|
210
|
-
|
|
212
|
+
|
|
211
213
|
return {
|
|
212
214
|
name: pubspec.name || '',
|
|
213
215
|
version: pubspec.version || '0.0.1',
|
|
@@ -220,42 +222,156 @@ function readFlutterPackageMetadata(packagePath: string): FlutterPackageMetadata
|
|
|
220
222
|
}
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
// Copy markdown docs that match .d.ts basenames from source to the built dist folder,
|
|
226
|
+
// and generate an aggregated README.md in the dist directory.
|
|
227
|
+
async function copyMarkdownDocsToDist(params: {
|
|
228
|
+
sourceRoot: string;
|
|
229
|
+
distRoot: string;
|
|
230
|
+
exclude?: string[];
|
|
231
|
+
}): Promise<{ copied: number; skipped: number }> {
|
|
232
|
+
const { sourceRoot, distRoot, exclude } = params;
|
|
233
|
+
|
|
234
|
+
// Ensure dist exists
|
|
235
|
+
if (!fs.existsSync(distRoot)) {
|
|
236
|
+
return { copied: 0, skipped: 0 };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Default ignore patterns similar to generator
|
|
240
|
+
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
|
|
241
|
+
const ignore = exclude && exclude.length ? [...defaultIgnore, ...exclude] : defaultIgnore;
|
|
242
|
+
|
|
243
|
+
// Find all .d.ts files and check for sibling .md files
|
|
244
|
+
const dtsFiles = glob.globSync('**/*.d.ts', { cwd: sourceRoot, ignore });
|
|
245
|
+
let copied = 0;
|
|
246
|
+
let skipped = 0;
|
|
247
|
+
const readmeSections: { title: string; relPath: string; content: string }[] = [];
|
|
248
|
+
|
|
249
|
+
for (const relDts of dtsFiles) {
|
|
250
|
+
if (path.basename(relDts) === 'global.d.ts') {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const relMd = relDts.replace(/\.d\.ts$/i, '.md');
|
|
255
|
+
const absMd = path.join(sourceRoot, relMd);
|
|
256
|
+
if (!fs.existsSync(absMd)) {
|
|
257
|
+
skipped++;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let content = '';
|
|
262
|
+
try {
|
|
263
|
+
content = fs.readFileSync(absMd, 'utf-8');
|
|
264
|
+
} catch {
|
|
265
|
+
// If we cannot read the file, still attempt to copy it and skip README aggregation for this entry.
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Copy into dist preserving relative path
|
|
269
|
+
const destPath = path.join(distRoot, relMd);
|
|
270
|
+
const destDir = path.dirname(destPath);
|
|
271
|
+
if (!fs.existsSync(destDir)) {
|
|
272
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
273
|
+
}
|
|
274
|
+
fs.copyFileSync(absMd, destPath);
|
|
275
|
+
copied++;
|
|
276
|
+
|
|
277
|
+
if (content) {
|
|
278
|
+
const base = path.basename(relMd, '.md');
|
|
279
|
+
const title = base
|
|
280
|
+
.split(/[-_]+/)
|
|
281
|
+
.filter(Boolean)
|
|
282
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
283
|
+
.join(' ');
|
|
284
|
+
readmeSections.push({
|
|
285
|
+
title: title || base,
|
|
286
|
+
relPath: relMd,
|
|
287
|
+
content
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Generate an aggregated README.md inside distRoot so consumers can see component docs easily.
|
|
293
|
+
if (readmeSections.length > 0) {
|
|
294
|
+
const readmePath = path.join(distRoot, 'README.md');
|
|
295
|
+
let existing = '';
|
|
296
|
+
if (fs.existsSync(readmePath)) {
|
|
297
|
+
try {
|
|
298
|
+
existing = fs.readFileSync(readmePath, 'utf-8');
|
|
299
|
+
} catch {
|
|
300
|
+
existing = '';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const headerLines: string[] = [
|
|
305
|
+
'# WebF Component Documentation',
|
|
306
|
+
'',
|
|
307
|
+
'> This README is generated from markdown docs co-located with TypeScript definitions in the Flutter package.',
|
|
308
|
+
''
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
const sectionBlocks = readmeSections.map(section => {
|
|
312
|
+
const lines: string[] = [];
|
|
313
|
+
lines.push(`## ${section.title}`);
|
|
314
|
+
lines.push('');
|
|
315
|
+
lines.push(`_Source: \`./${section.relPath}\`_`);
|
|
316
|
+
lines.push('');
|
|
317
|
+
lines.push(section.content.trim());
|
|
318
|
+
lines.push('');
|
|
319
|
+
return lines.join('\n');
|
|
320
|
+
}).join('\n');
|
|
321
|
+
|
|
322
|
+
let finalContent: string;
|
|
323
|
+
if (existing && existing.trim().length > 0) {
|
|
324
|
+
finalContent = `${existing.trim()}\n\n---\n\n${headerLines.join('\n')}${sectionBlocks}\n`;
|
|
325
|
+
} else {
|
|
326
|
+
finalContent = `${headerLines.join('\n')}${sectionBlocks}\n`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
fs.writeFileSync(readmePath, finalContent, 'utf-8');
|
|
331
|
+
} catch {
|
|
332
|
+
// If README generation fails, do not affect overall codegen.
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return { copied, skipped };
|
|
337
|
+
}
|
|
338
|
+
|
|
223
339
|
function validateTypeScriptEnvironment(projectPath: string): { isValid: boolean; errors: string[] } {
|
|
224
340
|
const errors: string[] = [];
|
|
225
|
-
|
|
341
|
+
|
|
226
342
|
// Check for TypeScript configuration
|
|
227
343
|
const tsConfigPath = path.join(projectPath, 'tsconfig.json');
|
|
228
344
|
if (!fs.existsSync(tsConfigPath)) {
|
|
229
345
|
errors.push('Missing tsconfig.json - TypeScript configuration is required for type definitions');
|
|
230
346
|
}
|
|
231
|
-
|
|
347
|
+
|
|
232
348
|
// Check for .d.ts files - this is critical
|
|
233
349
|
const libPath = path.join(projectPath, 'lib');
|
|
234
350
|
let hasDtsFiles = false;
|
|
235
|
-
|
|
351
|
+
|
|
236
352
|
if (fs.existsSync(libPath)) {
|
|
237
353
|
// Check in lib directory
|
|
238
|
-
hasDtsFiles = fs.readdirSync(libPath).some(file =>
|
|
239
|
-
file.endsWith('.d.ts') ||
|
|
240
|
-
(fs.statSync(path.join(libPath, file)).isDirectory() &&
|
|
354
|
+
hasDtsFiles = fs.readdirSync(libPath).some(file =>
|
|
355
|
+
file.endsWith('.d.ts') ||
|
|
356
|
+
(fs.statSync(path.join(libPath, file)).isDirectory() &&
|
|
241
357
|
fs.readdirSync(path.join(libPath, file)).some(f => f.endsWith('.d.ts')))
|
|
242
358
|
);
|
|
243
359
|
}
|
|
244
|
-
|
|
360
|
+
|
|
245
361
|
// Also check in root directory
|
|
246
362
|
if (!hasDtsFiles) {
|
|
247
|
-
hasDtsFiles = fs.readdirSync(projectPath).some(file =>
|
|
248
|
-
file.endsWith('.d.ts') ||
|
|
249
|
-
(fs.statSync(path.join(projectPath, file)).isDirectory() &&
|
|
250
|
-
file !== 'node_modules' &&
|
|
363
|
+
hasDtsFiles = fs.readdirSync(projectPath).some(file =>
|
|
364
|
+
file.endsWith('.d.ts') ||
|
|
365
|
+
(fs.statSync(path.join(projectPath, file)).isDirectory() &&
|
|
366
|
+
file !== 'node_modules' &&
|
|
251
367
|
fs.existsSync(path.join(projectPath, file, 'index.d.ts')))
|
|
252
368
|
);
|
|
253
369
|
}
|
|
254
|
-
|
|
370
|
+
|
|
255
371
|
if (!hasDtsFiles) {
|
|
256
372
|
errors.push('No TypeScript definition files (.d.ts) found in the project - Please create .d.ts files for your components');
|
|
257
373
|
}
|
|
258
|
-
|
|
374
|
+
|
|
259
375
|
return {
|
|
260
376
|
isValid: errors.length === 0,
|
|
261
377
|
errors
|
|
@@ -265,8 +381,8 @@ function validateTypeScriptEnvironment(projectPath: string): { isValid: boolean;
|
|
|
265
381
|
function createCommand(target: string, options: { framework: string; packageName: string; metadata?: FlutterPackageMetadata }): void {
|
|
266
382
|
const { framework, metadata } = options;
|
|
267
383
|
// Ensure package name is always valid
|
|
268
|
-
const packageName = isValidNpmPackageName(options.packageName)
|
|
269
|
-
? options.packageName
|
|
384
|
+
const packageName = isValidNpmPackageName(options.packageName)
|
|
385
|
+
? options.packageName
|
|
270
386
|
: sanitizePackageName(options.packageName);
|
|
271
387
|
|
|
272
388
|
if (!fs.existsSync(target)) {
|
|
@@ -310,7 +426,8 @@ function createCommand(target: string, options: { framework: string; packageName
|
|
|
310
426
|
// Leave merge to the codegen step which appends exports safely
|
|
311
427
|
}
|
|
312
428
|
|
|
313
|
-
|
|
429
|
+
// !no '--omit=peer' here.
|
|
430
|
+
spawnSync(NPM, ['install'], {
|
|
314
431
|
cwd: target,
|
|
315
432
|
stdio: 'inherit'
|
|
316
433
|
});
|
|
@@ -350,24 +467,30 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
350
467
|
// If distPath is not provided or is '.', create a temporary directory
|
|
351
468
|
let resolvedDistPath: string;
|
|
352
469
|
let isTempDir = false;
|
|
353
|
-
|
|
470
|
+
const isDartOnly = options.dartOnly;
|
|
471
|
+
|
|
354
472
|
if (!distPath || distPath === '.') {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
473
|
+
if (isDartOnly) {
|
|
474
|
+
// In Dart-only mode we don't need a temporary Node project directory
|
|
475
|
+
resolvedDistPath = path.resolve(distPath || '.');
|
|
476
|
+
} else {
|
|
477
|
+
// Create a temporary directory for the generated package
|
|
478
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webf-typings-'));
|
|
479
|
+
resolvedDistPath = tempDir;
|
|
480
|
+
isTempDir = true;
|
|
481
|
+
console.log(`\nUsing temporary directory: ${tempDir}`);
|
|
482
|
+
}
|
|
360
483
|
} else {
|
|
361
484
|
resolvedDistPath = path.resolve(distPath);
|
|
362
485
|
}
|
|
363
|
-
|
|
486
|
+
|
|
364
487
|
// First, check if we're in a Flutter package directory when flutter-package-src is not provided
|
|
365
488
|
if (!options.flutterPackageSrc) {
|
|
366
489
|
// Check if current directory or parent directories contain pubspec.yaml
|
|
367
490
|
let currentDir = process.cwd();
|
|
368
491
|
let foundPubspec = false;
|
|
369
492
|
let pubspecDir = '';
|
|
370
|
-
|
|
493
|
+
|
|
371
494
|
// Search up to 3 levels up for pubspec.yaml
|
|
372
495
|
for (let i = 0; i < 3; i++) {
|
|
373
496
|
const pubspecPath = path.join(currentDir, 'pubspec.yaml');
|
|
@@ -380,141 +503,151 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
380
503
|
if (parentDir === currentDir) break; // Reached root
|
|
381
504
|
currentDir = parentDir;
|
|
382
505
|
}
|
|
383
|
-
|
|
506
|
+
|
|
384
507
|
if (foundPubspec) {
|
|
385
508
|
// Use the directory containing pubspec.yaml as the flutter package source
|
|
386
509
|
options.flutterPackageSrc = pubspecDir;
|
|
387
510
|
console.log(`\nDetected Flutter package at: ${pubspecDir}`);
|
|
388
511
|
}
|
|
389
512
|
}
|
|
390
|
-
|
|
391
|
-
// Check if the directory exists and has required files
|
|
392
|
-
const packageJsonPath = path.join(resolvedDistPath, 'package.json');
|
|
393
|
-
const globalDtsPath = path.join(resolvedDistPath, 'global.d.ts');
|
|
394
|
-
const tsConfigPath = path.join(resolvedDistPath, 'tsconfig.json');
|
|
395
|
-
|
|
396
|
-
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
397
|
-
const hasGlobalDts = fs.existsSync(globalDtsPath);
|
|
398
|
-
const hasTsConfig = fs.existsSync(tsConfigPath);
|
|
399
|
-
|
|
400
|
-
// Determine if we need to create a new project
|
|
401
|
-
const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
|
|
402
|
-
|
|
403
|
-
// Track if this is an existing project (has all required files)
|
|
404
|
-
const isExistingProject = hasPackageJson && hasGlobalDts && hasTsConfig;
|
|
405
|
-
|
|
513
|
+
|
|
406
514
|
let framework = options.framework;
|
|
407
515
|
let packageName = options.packageName;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
516
|
+
let isExistingProject = false;
|
|
517
|
+
|
|
518
|
+
if (!isDartOnly) {
|
|
519
|
+
// Check if the directory exists and has required files
|
|
520
|
+
const packageJsonPath = path.join(resolvedDistPath, 'package.json');
|
|
521
|
+
const globalDtsPath = path.join(resolvedDistPath, 'global.d.ts');
|
|
522
|
+
const tsConfigPath = path.join(resolvedDistPath, 'tsconfig.json');
|
|
523
|
+
|
|
524
|
+
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
525
|
+
const hasGlobalDts = fs.existsSync(globalDtsPath);
|
|
526
|
+
const hasTsConfig = fs.existsSync(tsConfigPath);
|
|
527
|
+
|
|
528
|
+
// Determine if we need to create a new project
|
|
529
|
+
const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
|
|
530
|
+
|
|
531
|
+
// Track if this is an existing project (has all required files)
|
|
532
|
+
isExistingProject = hasPackageJson && hasGlobalDts && hasTsConfig;
|
|
533
|
+
|
|
534
|
+
// Validate and sanitize package name if provided
|
|
535
|
+
if (packageName && !isValidNpmPackageName(packageName)) {
|
|
536
|
+
console.warn(`Warning: Package name "${packageName}" is not valid for npm.`);
|
|
537
|
+
const sanitized = sanitizePackageName(packageName);
|
|
538
|
+
console.log(`Using sanitized name: "${sanitized}"`);
|
|
539
|
+
packageName = sanitized;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (needsProjectCreation) {
|
|
543
|
+
// If project needs creation but options are missing, prompt for them
|
|
544
|
+
if (!framework) {
|
|
545
|
+
const frameworkAnswer = await inquirer.prompt([{
|
|
546
|
+
type: 'list',
|
|
547
|
+
name: 'framework',
|
|
548
|
+
message: 'Which framework would you like to use?',
|
|
549
|
+
choices: ['react', 'vue']
|
|
550
|
+
}]);
|
|
551
|
+
framework = frameworkAnswer.framework;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Try to read Flutter package metadata if flutterPackageSrc is provided
|
|
555
|
+
let metadata: FlutterPackageMetadata | null = null;
|
|
556
|
+
if (options.flutterPackageSrc) {
|
|
557
|
+
metadata = readFlutterPackageMetadata(options.flutterPackageSrc);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (!packageName) {
|
|
561
|
+
// Use Flutter package name as default if available, sanitized for npm
|
|
562
|
+
const rawDefaultName = metadata?.name || path.basename(resolvedDistPath);
|
|
563
|
+
const defaultPackageName = sanitizePackageName(rawDefaultName);
|
|
564
|
+
|
|
565
|
+
const packageNameAnswer = await inquirer.prompt([{
|
|
566
|
+
type: 'input',
|
|
567
|
+
name: 'packageName',
|
|
568
|
+
message: 'What is your package name?',
|
|
569
|
+
default: defaultPackageName,
|
|
570
|
+
validate: (input: string) => {
|
|
571
|
+
if (!input || input.trim() === '') {
|
|
572
|
+
return 'Package name is required';
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Check if it's valid as-is
|
|
576
|
+
if (isValidNpmPackageName(input)) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// If not valid, show what it would be sanitized to
|
|
581
|
+
const sanitized = sanitizePackageName(input);
|
|
582
|
+
return `Invalid npm package name. Would be sanitized to: "${sanitized}". Please enter a valid name.`;
|
|
453
583
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
message: 'Which framework are you using?',
|
|
487
|
-
choices: ['react', 'vue']
|
|
488
|
-
}]);
|
|
489
|
-
framework = frameworkAnswer.framework;
|
|
584
|
+
}]);
|
|
585
|
+
packageName = packageNameAnswer.packageName;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
console.log(`\nCreating new ${framework} project in ${resolvedDistPath}...`);
|
|
589
|
+
createCommand(resolvedDistPath, {
|
|
590
|
+
framework: framework!,
|
|
591
|
+
packageName: packageName!,
|
|
592
|
+
metadata: metadata || undefined
|
|
593
|
+
});
|
|
594
|
+
} else {
|
|
595
|
+
// Validate existing project structure
|
|
596
|
+
if (hasPackageJson) {
|
|
597
|
+
try {
|
|
598
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
599
|
+
|
|
600
|
+
// Detect framework from existing package.json
|
|
601
|
+
if (!framework) {
|
|
602
|
+
if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
|
|
603
|
+
framework = 'react';
|
|
604
|
+
} else if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
|
|
605
|
+
framework = 'vue';
|
|
606
|
+
} else {
|
|
607
|
+
// If can't detect, prompt for it
|
|
608
|
+
const frameworkAnswer = await inquirer.prompt([{
|
|
609
|
+
type: 'list',
|
|
610
|
+
name: 'framework',
|
|
611
|
+
message: 'Which framework are you using?',
|
|
612
|
+
choices: ['react', 'vue']
|
|
613
|
+
}]);
|
|
614
|
+
framework = frameworkAnswer.framework;
|
|
615
|
+
}
|
|
490
616
|
}
|
|
617
|
+
|
|
618
|
+
console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
|
|
619
|
+
} catch (e) {
|
|
620
|
+
console.error('Error reading package.json:', e);
|
|
621
|
+
process.exit(1);
|
|
491
622
|
}
|
|
492
|
-
|
|
493
|
-
console.log(`\nDetected existing ${framework} project in ${resolvedDistPath}`);
|
|
494
|
-
} catch (e) {
|
|
495
|
-
console.error('Error reading package.json:', e);
|
|
496
|
-
process.exit(1);
|
|
497
623
|
}
|
|
498
624
|
}
|
|
625
|
+
} else {
|
|
626
|
+
// In Dart-only mode, framework/packageName are unused; ensure framework is not accidentally required later.
|
|
627
|
+
framework = options.framework;
|
|
499
628
|
}
|
|
500
|
-
|
|
629
|
+
|
|
501
630
|
// Now proceed with code generation if flutter package source is provided
|
|
502
631
|
if (!options.flutterPackageSrc) {
|
|
503
632
|
console.log('\nProject is ready for code generation.');
|
|
504
633
|
console.log('To generate code, run:');
|
|
505
634
|
const displayPath = isTempDir ? '<output-dir>' : distPath;
|
|
506
|
-
|
|
635
|
+
if (isDartOnly) {
|
|
636
|
+
console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --dart-only`);
|
|
637
|
+
} else {
|
|
638
|
+
console.log(` webf codegen ${displayPath} --flutter-package-src=<path> --framework=${framework}`);
|
|
639
|
+
}
|
|
507
640
|
if (isTempDir) {
|
|
508
641
|
// Clean up temporary directory if we're not using it
|
|
509
642
|
fs.rmSync(resolvedDistPath, { recursive: true, force: true });
|
|
510
643
|
}
|
|
511
644
|
return;
|
|
512
645
|
}
|
|
513
|
-
|
|
646
|
+
|
|
514
647
|
// Validate TypeScript environment in the Flutter package
|
|
515
648
|
console.log(`\nValidating TypeScript environment in ${options.flutterPackageSrc}...`);
|
|
516
649
|
const validation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
517
|
-
|
|
650
|
+
|
|
518
651
|
if (!validation.isValid) {
|
|
519
652
|
// Check specifically for missing tsconfig.json
|
|
520
653
|
const tsConfigPath = path.join(options.flutterPackageSrc, 'tsconfig.json');
|
|
@@ -525,7 +658,7 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
525
658
|
message: 'No tsconfig.json found. Would you like me to create one for you?',
|
|
526
659
|
default: true
|
|
527
660
|
}]);
|
|
528
|
-
|
|
661
|
+
|
|
529
662
|
if (createTsConfigAnswer.createTsConfig) {
|
|
530
663
|
// Create a default tsconfig.json
|
|
531
664
|
const defaultTsConfig = {
|
|
@@ -544,10 +677,10 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
544
677
|
include: ['lib/**/*.d.ts', '**/*.d.ts'],
|
|
545
678
|
exclude: ['node_modules', 'dist', 'build']
|
|
546
679
|
};
|
|
547
|
-
|
|
680
|
+
|
|
548
681
|
fs.writeFileSync(tsConfigPath, JSON.stringify(defaultTsConfig, null, 2), 'utf-8');
|
|
549
682
|
console.log('ā
Created tsconfig.json');
|
|
550
|
-
|
|
683
|
+
|
|
551
684
|
// Re-validate after creating tsconfig
|
|
552
685
|
const newValidation = validateTypeScriptEnvironment(options.flutterPackageSrc);
|
|
553
686
|
if (!newValidation.isValid) {
|
|
@@ -569,21 +702,40 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
569
702
|
process.exit(1);
|
|
570
703
|
}
|
|
571
704
|
}
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
|
|
705
|
+
|
|
706
|
+
const baseCommand = 'webf codegen';
|
|
707
|
+
const flutterPart = options.flutterPackageSrc ? ` --flutter-package-src=${options.flutterPackageSrc}` : '';
|
|
708
|
+
const modePart = isDartOnly
|
|
709
|
+
? ' --dart-only'
|
|
710
|
+
: (framework ? ` --framework=${framework}` : '');
|
|
711
|
+
const command = `${baseCommand}${flutterPart}${modePart} <distPath>`;
|
|
712
|
+
|
|
713
|
+
if (isDartOnly) {
|
|
714
|
+
console.log(`\nGenerating Dart bindings from ${options.flutterPackageSrc}...`);
|
|
715
|
+
|
|
716
|
+
await dartGen({
|
|
717
|
+
source: options.flutterPackageSrc,
|
|
718
|
+
target: options.flutterPackageSrc,
|
|
719
|
+
command,
|
|
720
|
+
exclude: options.exclude,
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
console.log('\nDart code generation completed successfully!');
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
575
727
|
// Auto-initialize typings in the output directory if needed
|
|
576
728
|
ensureInitialized(resolvedDistPath);
|
|
577
|
-
|
|
729
|
+
|
|
578
730
|
console.log(`\nGenerating ${framework} code from ${options.flutterPackageSrc}...`);
|
|
579
|
-
|
|
731
|
+
|
|
580
732
|
await dartGen({
|
|
581
733
|
source: options.flutterPackageSrc,
|
|
582
734
|
target: options.flutterPackageSrc,
|
|
583
735
|
command,
|
|
584
736
|
exclude: options.exclude,
|
|
585
737
|
});
|
|
586
|
-
|
|
738
|
+
|
|
587
739
|
if (framework === 'react') {
|
|
588
740
|
// Get the package name from package.json if it exists
|
|
589
741
|
let reactPackageName: string | undefined;
|
|
@@ -596,13 +748,15 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
596
748
|
} catch (e) {
|
|
597
749
|
// Ignore errors
|
|
598
750
|
}
|
|
599
|
-
|
|
751
|
+
|
|
600
752
|
await reactGen({
|
|
601
753
|
source: options.flutterPackageSrc,
|
|
602
754
|
target: resolvedDistPath,
|
|
603
755
|
command,
|
|
604
756
|
exclude: options.exclude,
|
|
605
|
-
packageName
|
|
757
|
+
// Prefer CLI-provided packageName (validated/sanitized above),
|
|
758
|
+
// fallback to detected name from package.json
|
|
759
|
+
packageName: packageName || reactPackageName,
|
|
606
760
|
});
|
|
607
761
|
} else if (framework === 'vue') {
|
|
608
762
|
await vueGen({
|
|
@@ -612,19 +766,31 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
612
766
|
exclude: options.exclude,
|
|
613
767
|
});
|
|
614
768
|
}
|
|
615
|
-
|
|
769
|
+
|
|
616
770
|
console.log('\nCode generation completed successfully!');
|
|
617
|
-
|
|
771
|
+
|
|
618
772
|
// Automatically build the generated package
|
|
619
773
|
if (framework) {
|
|
620
774
|
try {
|
|
621
775
|
await buildPackage(resolvedDistPath);
|
|
776
|
+
// After building React package, copy any matching .md docs next to built JS files
|
|
777
|
+
if (framework === 'react' && options.flutterPackageSrc) {
|
|
778
|
+
const distOut = path.join(resolvedDistPath, 'dist');
|
|
779
|
+
const { copied } = await copyMarkdownDocsToDist({
|
|
780
|
+
sourceRoot: options.flutterPackageSrc,
|
|
781
|
+
distRoot: distOut,
|
|
782
|
+
exclude: options.exclude,
|
|
783
|
+
});
|
|
784
|
+
if (copied > 0) {
|
|
785
|
+
console.log(`š Copied ${copied} markdown docs to dist`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
622
788
|
} catch (error) {
|
|
623
789
|
console.error('\nWarning: Build failed:', error);
|
|
624
790
|
// Don't exit here since generation was successful
|
|
625
791
|
}
|
|
626
792
|
}
|
|
627
|
-
|
|
793
|
+
|
|
628
794
|
// Handle npm publishing if requested via command line option
|
|
629
795
|
if (options.publishToNpm && framework) {
|
|
630
796
|
try {
|
|
@@ -641,7 +807,7 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
641
807
|
message: 'Would you like to publish this package to npm?',
|
|
642
808
|
default: false
|
|
643
809
|
}]);
|
|
644
|
-
|
|
810
|
+
|
|
645
811
|
if (publishAnswer.publish) {
|
|
646
812
|
// Ask for registry
|
|
647
813
|
const registryAnswer = await inquirer.prompt([{
|
|
@@ -659,10 +825,10 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
659
825
|
}
|
|
660
826
|
}
|
|
661
827
|
}]);
|
|
662
|
-
|
|
828
|
+
|
|
663
829
|
try {
|
|
664
830
|
await buildAndPublishPackage(
|
|
665
|
-
resolvedDistPath,
|
|
831
|
+
resolvedDistPath,
|
|
666
832
|
registryAnswer.registry || undefined,
|
|
667
833
|
isExistingProject
|
|
668
834
|
);
|
|
@@ -672,7 +838,7 @@ async function generateCommand(distPath: string, options: GenerateOptions): Prom
|
|
|
672
838
|
}
|
|
673
839
|
}
|
|
674
840
|
}
|
|
675
|
-
|
|
841
|
+
|
|
676
842
|
// If using a temporary directory, remind the user where the files are
|
|
677
843
|
if (isTempDir) {
|
|
678
844
|
console.log(`\nš Generated files are in: ${resolvedDistPath}`);
|
|
@@ -694,19 +860,19 @@ function writeFileIfChanged(filePath: string, content: string) {
|
|
|
694
860
|
function ensureInitialized(targetPath: string): void {
|
|
695
861
|
const globalDtsPath = path.join(targetPath, 'global.d.ts');
|
|
696
862
|
const tsConfigPath = path.join(targetPath, 'tsconfig.json');
|
|
697
|
-
|
|
863
|
+
|
|
698
864
|
// Check if initialization files already exist
|
|
699
865
|
const needsInit = !fs.existsSync(globalDtsPath) || !fs.existsSync(tsConfigPath);
|
|
700
|
-
|
|
866
|
+
|
|
701
867
|
if (needsInit) {
|
|
702
868
|
console.log('Initializing WebF typings...');
|
|
703
869
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
704
|
-
|
|
870
|
+
|
|
705
871
|
if (!fs.existsSync(globalDtsPath)) {
|
|
706
872
|
fs.writeFileSync(globalDtsPath, gloabalDts, 'utf-8');
|
|
707
873
|
console.log('Created global.d.ts');
|
|
708
874
|
}
|
|
709
|
-
|
|
875
|
+
|
|
710
876
|
if (!fs.existsSync(tsConfigPath)) {
|
|
711
877
|
fs.writeFileSync(tsConfigPath, tsConfig, 'utf-8');
|
|
712
878
|
console.log('Created tsconfig.json');
|
|
@@ -716,7 +882,7 @@ function ensureInitialized(targetPath: string): void {
|
|
|
716
882
|
|
|
717
883
|
async function buildPackage(packagePath: string): Promise<void> {
|
|
718
884
|
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
719
|
-
|
|
885
|
+
|
|
720
886
|
if (!fs.existsSync(packageJsonPath)) {
|
|
721
887
|
// Skip the error in test environment to avoid console warnings
|
|
722
888
|
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
|
|
@@ -724,34 +890,34 @@ async function buildPackage(packagePath: string): Promise<void> {
|
|
|
724
890
|
}
|
|
725
891
|
throw new Error(`No package.json found in ${packagePath}`);
|
|
726
892
|
}
|
|
727
|
-
|
|
893
|
+
|
|
728
894
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
729
895
|
const packageName = packageJson.name;
|
|
730
896
|
const packageVersion = packageJson.version;
|
|
731
|
-
|
|
897
|
+
|
|
732
898
|
// Check if node_modules exists
|
|
733
899
|
const nodeModulesPath = path.join(packagePath, 'node_modules');
|
|
734
900
|
if (!fs.existsSync(nodeModulesPath)) {
|
|
735
901
|
console.log(`\nš¦ Installing dependencies for ${packageName}...`);
|
|
736
|
-
|
|
902
|
+
|
|
737
903
|
// Check if yarn.lock exists to determine package manager
|
|
738
904
|
const yarnLockPath = path.join(packagePath, 'yarn.lock');
|
|
739
905
|
const useYarn = fs.existsSync(yarnLockPath);
|
|
740
|
-
|
|
906
|
+
|
|
741
907
|
const installCommand = useYarn ? 'yarn' : NPM;
|
|
742
908
|
const installArgs = useYarn ? [] : ['install'];
|
|
743
|
-
|
|
909
|
+
|
|
744
910
|
const installResult = spawnSync(installCommand, installArgs, {
|
|
745
911
|
cwd: packagePath,
|
|
746
912
|
stdio: 'inherit'
|
|
747
913
|
});
|
|
748
|
-
|
|
914
|
+
|
|
749
915
|
if (installResult.status !== 0) {
|
|
750
916
|
throw new Error('Failed to install dependencies');
|
|
751
917
|
}
|
|
752
918
|
console.log('ā
Dependencies installed successfully!');
|
|
753
919
|
}
|
|
754
|
-
|
|
920
|
+
|
|
755
921
|
// Check if package has a build script
|
|
756
922
|
if (packageJson.scripts?.build) {
|
|
757
923
|
console.log(`\nBuilding ${packageName}@${packageVersion}...`);
|
|
@@ -759,7 +925,7 @@ async function buildPackage(packagePath: string): Promise<void> {
|
|
|
759
925
|
cwd: packagePath,
|
|
760
926
|
stdio: 'inherit'
|
|
761
927
|
});
|
|
762
|
-
|
|
928
|
+
|
|
763
929
|
if (buildResult.status !== 0) {
|
|
764
930
|
throw new Error('Build failed');
|
|
765
931
|
}
|
|
@@ -771,18 +937,18 @@ async function buildPackage(packagePath: string): Promise<void> {
|
|
|
771
937
|
|
|
772
938
|
async function buildAndPublishPackage(packagePath: string, registry?: string, isExistingProject: boolean = false): Promise<void> {
|
|
773
939
|
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
774
|
-
|
|
940
|
+
|
|
775
941
|
if (!fs.existsSync(packageJsonPath)) {
|
|
776
942
|
throw new Error(`No package.json found in ${packagePath}`);
|
|
777
943
|
}
|
|
778
|
-
|
|
944
|
+
|
|
779
945
|
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
780
946
|
const packageName = packageJson.name;
|
|
781
947
|
let packageVersion = packageJson.version;
|
|
782
|
-
|
|
948
|
+
|
|
783
949
|
// First, ensure dependencies are installed and build the package
|
|
784
950
|
await buildPackage(packagePath);
|
|
785
|
-
|
|
951
|
+
|
|
786
952
|
// If this is an existing project, increment the patch version before publishing
|
|
787
953
|
if (isExistingProject) {
|
|
788
954
|
console.log(`\nIncrementing version for existing project...`);
|
|
@@ -791,18 +957,18 @@ async function buildAndPublishPackage(packagePath: string, registry?: string, is
|
|
|
791
957
|
encoding: 'utf-8',
|
|
792
958
|
stdio: 'pipe'
|
|
793
959
|
});
|
|
794
|
-
|
|
960
|
+
|
|
795
961
|
if (versionResult.status !== 0) {
|
|
796
962
|
console.error('Failed to increment version:', versionResult.stderr);
|
|
797
963
|
throw new Error('Failed to increment version');
|
|
798
964
|
}
|
|
799
|
-
|
|
965
|
+
|
|
800
966
|
// Re-read package.json to get the new version
|
|
801
967
|
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
802
968
|
packageVersion = packageJson.version;
|
|
803
969
|
console.log(`Version updated to ${packageVersion}`);
|
|
804
970
|
}
|
|
805
|
-
|
|
971
|
+
|
|
806
972
|
// Set registry if provided
|
|
807
973
|
if (registry) {
|
|
808
974
|
console.log(`\nUsing npm registry: ${registry}`);
|
|
@@ -810,38 +976,38 @@ async function buildAndPublishPackage(packagePath: string, registry?: string, is
|
|
|
810
976
|
cwd: packagePath,
|
|
811
977
|
stdio: 'inherit'
|
|
812
978
|
});
|
|
813
|
-
|
|
979
|
+
|
|
814
980
|
if (setRegistryResult.status !== 0) {
|
|
815
981
|
throw new Error('Failed to set npm registry');
|
|
816
982
|
}
|
|
817
983
|
}
|
|
818
|
-
|
|
984
|
+
|
|
819
985
|
// Check if user is logged in to npm
|
|
820
986
|
const whoamiResult = spawnSync(NPM, ['whoami'], {
|
|
821
987
|
cwd: packagePath,
|
|
822
988
|
encoding: 'utf-8'
|
|
823
989
|
});
|
|
824
|
-
|
|
990
|
+
|
|
825
991
|
if (whoamiResult.status !== 0) {
|
|
826
992
|
console.error('\nError: You must be logged in to npm to publish packages.');
|
|
827
993
|
console.error('Please run "npm login" first.');
|
|
828
994
|
throw new Error('Not logged in to npm');
|
|
829
995
|
}
|
|
830
|
-
|
|
996
|
+
|
|
831
997
|
console.log(`\nPublishing ${packageName}@${packageVersion} to npm...`);
|
|
832
|
-
|
|
998
|
+
|
|
833
999
|
// Publish the package
|
|
834
1000
|
const publishResult = spawnSync(NPM, ['publish'], {
|
|
835
1001
|
cwd: packagePath,
|
|
836
1002
|
stdio: 'inherit'
|
|
837
1003
|
});
|
|
838
|
-
|
|
1004
|
+
|
|
839
1005
|
if (publishResult.status !== 0) {
|
|
840
1006
|
throw new Error('Publish failed');
|
|
841
1007
|
}
|
|
842
|
-
|
|
1008
|
+
|
|
843
1009
|
console.log(`\nā
Successfully published ${packageName}@${packageVersion}`);
|
|
844
|
-
|
|
1010
|
+
|
|
845
1011
|
// Reset registry to default if it was changed
|
|
846
1012
|
if (registry) {
|
|
847
1013
|
spawnSync(NPM, ['config', 'delete', 'registry'], {
|