@jspreadsheet/install 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/index.js +459 -322
  2. package/package.json +23 -21
package/index.js CHANGED
@@ -1,322 +1,459 @@
1
- #!/usr/bin/env node
2
-
3
- const prompts = require('prompts');
4
- const { execSync } = require('child_process');
5
- const https = require('https');
6
-
7
- const PACKAGES = {
8
- pro: {
9
- vanilla: 'jspreadsheet',
10
- react: '@jspreadsheet/react',
11
- vue: '@jspreadsheet/vue',
12
- angular: 'jspreadsheet',
13
- },
14
- ce: {
15
- vanilla: 'jspreadsheet-ce',
16
- react: '@jspreadsheet-ce/react',
17
- vue: '@jspreadsheet-ce/vue',
18
- angular: 'jspreadsheet-ce',
19
- },
20
- };
21
-
22
- /**
23
- * Detect the package manager used in the current project
24
- * @returns {string}
25
- */
26
- function detectPackageManager() {
27
- const fs = require('fs');
28
- const path = require('path');
29
- const cwd = process.cwd();
30
-
31
- if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
32
- return 'yarn';
33
- }
34
- if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
35
- return 'pnpm';
36
- }
37
- if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {
38
- return 'bun';
39
- }
40
- return 'npm';
41
- }
42
-
43
- /**
44
- * Build the install command for a given package
45
- * @param {string} pkg
46
- * @returns {string}
47
- */
48
- function getInstallCommand(pkg) {
49
- const pm = detectPackageManager();
50
- if (pm === 'yarn') {
51
- return `yarn add ${pkg}`;
52
- }
53
- if (pm === 'pnpm') {
54
- return `pnpm add ${pkg}`;
55
- }
56
- if (pm === 'bun') {
57
- return `bun add ${pkg}`;
58
- }
59
- return `npm install ${pkg}`;
60
- }
61
-
62
- /**
63
- * Register email and get a trial license
64
- * @param {string} email
65
- * @returns {Promise<string|null>}
66
- */
67
- function registerEmail(email) {
68
- return new Promise((resolve) => {
69
- const data = JSON.stringify({ email });
70
-
71
- const options = {
72
- hostname: 'jspreadsheet.com',
73
- port: 443,
74
- path: '/me/register',
75
- method: 'POST',
76
- headers: {
77
- 'Content-Type': 'application/json',
78
- 'Content-Length': Buffer.byteLength(data),
79
- },
80
- };
81
-
82
- const req = https.request(options, (res) => {
83
- let body = '';
84
- res.on('data', (chunk) => body += chunk);
85
- res.on('end', () => {
86
- try {
87
- const result = JSON.parse(body);
88
- resolve(result.license || null);
89
- } catch (e) {
90
- resolve(null);
91
- }
92
- });
93
- });
94
-
95
- req.on('error', () => resolve(null));
96
- req.write(data);
97
- req.end();
98
- });
99
- }
100
-
101
- async function main() {
102
- console.log('');
103
- console.log('');
104
- console.log(' Welcome to Jspreadsheet');
105
- console.log('');
106
- console.log(' The JavaScript data grid component for building');
107
- console.log(' enterprise-grade spreadsheet applications.');
108
- console.log('');
109
- console.log('');
110
-
111
- // Step 1: Choose distribution
112
- const { distribution } = await prompts({
113
- type: 'select',
114
- name: 'distribution',
115
- message: 'Choose a distribution',
116
- choices: [
117
- {
118
- title: 'Jspreadsheet Pro',
119
- description: 'High-performance data grids with advanced Excel and Google Sheets compatibility. Ideal for enterprise and data-intensive applications.',
120
- value: 'pro',
121
- },
122
- {
123
- title: 'Jspreadsheet CE (Community Edition)',
124
- description: 'Lightweight and open source. Great for prototypes, MVPs, and community-driven projects.',
125
- value: 'ce',
126
- },
127
- ],
128
- });
129
-
130
- if (!distribution) {
131
- console.log('\nSetup cancelled.');
132
- process.exit(0);
133
- }
134
-
135
- console.log('');
136
-
137
- // Step 2: Choose framework
138
- const { framework } = await prompts({
139
- type: 'select',
140
- name: 'framework',
141
- message: 'Select your framework',
142
- choices: [
143
- { title: 'React', value: 'react' },
144
- { title: 'Vue', value: 'vue' },
145
- { title: 'Angular', value: 'angular' },
146
- { title: 'Vanilla JavaScript', value: 'vanilla' },
147
- { title: 'Other', value: 'vanilla' },
148
- ],
149
- });
150
-
151
- if (!framework) {
152
- console.log('\nSetup cancelled.');
153
- process.exit(0);
154
- }
155
-
156
- const pkg = PACKAGES[distribution][framework];
157
-
158
- // Step 3: Pro-specific registration flow
159
- let license = null;
160
-
161
- if (distribution === 'pro') {
162
- console.log('');
163
- const { support } = await prompts({
164
- type: 'select',
165
- name: 'support',
166
- message: 'Get free dedicated technical support for 30 days?',
167
- choices: [
168
- {
169
- title: 'Yes, register with my email',
170
- description: 'Receive a license key and priority support from the Jspreadsheet team.',
171
- value: 'register',
172
- },
173
- {
174
- title: 'No thanks, start evaluation without registration',
175
- description: 'You can register anytime later.',
176
- value: 'skip',
177
- },
178
- ],
179
- });
180
-
181
- if (!support) {
182
- console.log('\nSetup cancelled.');
183
- process.exit(0);
184
- }
185
-
186
- if (support === 'register') {
187
- const { email } = await prompts({
188
- type: 'text',
189
- name: 'email',
190
- message: 'Your email',
191
- validate: (value) => {
192
- if (!value) {
193
- return 'Email is required';
194
- }
195
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
196
- return 'Please enter a valid email address';
197
- }
198
- return true;
199
- },
200
- });
201
-
202
- if (!email) {
203
- console.log('\nSetup cancelled.');
204
- process.exit(0);
205
- }
206
-
207
- console.log('\n Registering...');
208
- license = await registerEmail(email);
209
-
210
- if (license) {
211
- console.log(' Done! Your license key has been generated.\n');
212
- } else {
213
- console.log(' Could not register at this time. You can register later at:');
214
- console.log(' https://jspreadsheet.com/me\n');
215
- console.log(' Continuing with evaluation mode.\n');
216
- }
217
- }
218
- }
219
-
220
- // Step 4: Install the package
221
- const cmd = getInstallCommand(pkg);
222
- console.log('');
223
- console.log(` Installing ${pkg}...`);
224
- console.log(` > ${cmd}`);
225
- console.log('');
226
-
227
- try {
228
- execSync(cmd, { stdio: 'inherit' });
229
- } catch (e) {
230
- console.error(`\n Installation failed. Try running manually:`);
231
- console.error(` ${cmd}\n`);
232
- process.exit(1);
233
- }
234
-
235
- // Step 5: Show post-install instructions
236
- console.log('');
237
- console.log('');
238
- console.log(' -----------------------------------------------');
239
- console.log(' All set! Jspreadsheet has been installed.');
240
- console.log(' -----------------------------------------------');
241
- console.log('');
242
- console.log('');
243
-
244
- const isPro = distribution === 'pro';
245
- const mainPkg = isPro ? 'jspreadsheet' : 'jspreadsheet-ce';
246
-
247
- if (isPro) {
248
- if (license) {
249
- console.log(' Activate your license:');
250
- console.log('');
251
- console.log(` jspreadsheet.setLicense('${license}');`);
252
- } else {
253
- console.log(' Start in evaluation mode:');
254
- console.log('');
255
- console.log(" jspreadsheet.setLicense('evaluation');");
256
- }
257
- console.log('');
258
- console.log('');
259
- if (!license) {
260
- console.log(' Want a license key? Register at:');
261
- console.log(' https://jspreadsheet.com/me');
262
- console.log('');
263
- console.log('');
264
- }
265
- console.log(' Need a commercial license?');
266
- console.log(' https://jspreadsheet.com/pricing');
267
- } else {
268
- console.log(' Jspreadsheet CE is ready to use.');
269
- }
270
-
271
- console.log('');
272
- console.log('');
273
-
274
- // Show framework-specific quick start
275
- console.log(' Getting started:');
276
- console.log('');
277
-
278
- if (framework === 'react') {
279
- const wrapper = isPro ? '@jspreadsheet/react' : '@jspreadsheet-ce/react';
280
- console.log(` import { Spreadsheet, Worksheet } from '${wrapper}';`);
281
- if (isPro) {
282
- console.log(` import jspreadsheet from '${mainPkg}';`);
283
- console.log('');
284
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
285
- }
286
- } else if (framework === 'vue') {
287
- const wrapper = isPro ? '@jspreadsheet/vue' : '@jspreadsheet-ce/vue';
288
- console.log(` import { Spreadsheet, Worksheet } from '${wrapper}';`);
289
- if (isPro) {
290
- console.log(` import jspreadsheet from '${mainPkg}';`);
291
- console.log('');
292
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
293
- }
294
- } else {
295
- console.log(` import jspreadsheet from '${mainPkg}';`);
296
- if (isPro) {
297
- console.log('');
298
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
299
- }
300
- }
301
-
302
- console.log('');
303
- console.log('');
304
-
305
- if (isPro) {
306
- console.log(' Docs: https://jspreadsheet.com/docs');
307
- } else {
308
- console.log(' Docs: https://bossanova.uk/jspreadsheet/docs');
309
- console.log('');
310
- console.log('');
311
- console.log(' Need file import/export, advanced formulas, or more features?');
312
- console.log(' Try Jspreadsheet Pro: https://jspreadsheet.com');
313
- }
314
-
315
- console.log('');
316
- console.log('');
317
- }
318
-
319
- main().catch((err) => {
320
- console.error(err);
321
- process.exit(1);
322
- });
1
+ #!/usr/bin/env node
2
+
3
+ const { select, input, Separator } = require('@inquirer/prompts');
4
+ const { execSync } = require('child_process');
5
+ const https = require('https');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ // Colors — use yoctocolors-cjs (transitive dep of @inquirer)
10
+ let c;
11
+ try {
12
+ c = require('yoctocolors-cjs');
13
+ } catch {
14
+ const esc = (code) => (s) => `\x1b[${code[0]}m${s}\x1b[${code[1]}m`;
15
+ c = {
16
+ bold: esc([1, 22]), dim: esc([2, 22]), italic: esc([3, 23]),
17
+ green: esc([32, 39]), red: esc([31, 39]), yellow: esc([33, 39]),
18
+ blue: esc([34, 39]), cyan: esc([36, 39]), gray: esc([90, 39]),
19
+ magenta: esc([35, 39]),
20
+ };
21
+ }
22
+
23
+ // ─── Constants ───────────────────────────────────────────────────────
24
+
25
+ const PACKAGES = {
26
+ pro: {
27
+ vanilla: 'jspreadsheet',
28
+ react: '@jspreadsheet/react',
29
+ vue: '@jspreadsheet/vue',
30
+ angular: 'jspreadsheet',
31
+ },
32
+ ce: {
33
+ vanilla: 'jspreadsheet-ce',
34
+ react: '@jspreadsheet-ce/react',
35
+ vue: '@jspreadsheet-ce/vue',
36
+ angular: 'jspreadsheet-ce',
37
+ },
38
+ };
39
+
40
+ const VALID_EDITIONS = ['pro', 'ce'];
41
+ const VALID_FRAMEWORKS = ['react', 'vue', 'angular', 'vanilla'];
42
+
43
+ // ─── Arg Parsing ─────────────────────────────────────────────────────
44
+
45
+ function parseArgs() {
46
+ const args = process.argv.slice(2);
47
+ const flags = {};
48
+ for (let i = 0; i < args.length; i++) {
49
+ const arg = args[i];
50
+ if ((arg === '--edition' || arg === '-e') && args[i + 1]) {
51
+ flags.edition = args[++i].toLowerCase();
52
+ } else if ((arg === '--framework' || arg === '-f') && args[i + 1]) {
53
+ flags.framework = args[++i].toLowerCase();
54
+ } else if (arg === '--email' && args[i + 1]) {
55
+ flags.email = args[++i];
56
+ } else if (arg === '--skip-registration' || arg === '--no-register') {
57
+ flags.skipRegistration = true;
58
+ } else if (arg === '--dry-run') {
59
+ flags.dryRun = true;
60
+ } else if (arg === '--help' || arg === '-h') {
61
+ flags.help = true;
62
+ } else if (arg === '--version' || arg === '-v') {
63
+ flags.version = true;
64
+ }
65
+ }
66
+ return flags;
67
+ }
68
+
69
+ function printHelp() {
70
+ console.log('');
71
+ console.log(` ${c.bold('Jspreadsheet Installer')}`);
72
+ console.log('');
73
+ console.log(' Usage:');
74
+ console.log(` ${c.dim('$')} npx @jspreadsheet/install ${c.dim('[options]')}`);
75
+ console.log('');
76
+ console.log(' Options:');
77
+ console.log(` ${c.cyan('--edition, -e')} ${c.dim('<pro|ce>')} Edition to install`);
78
+ console.log(` ${c.cyan('--framework, -f')} ${c.dim('<name>')} Framework: react, vue, angular, vanilla`);
79
+ console.log(` ${c.cyan('--email')} ${c.dim('<email>')} Register for free trial (Pro only)`);
80
+ console.log(` ${c.cyan('--skip-registration')} Skip registration prompt (Pro only)`);
81
+ console.log(` ${c.cyan('--dry-run')} Preview the flow without installing`);
82
+ console.log(` ${c.cyan('--version, -v')} Show version number`);
83
+ console.log(` ${c.cyan('--help, -h')} Show this help message`);
84
+ console.log('');
85
+ console.log(' Examples:');
86
+ console.log(` ${c.dim('$')} npx @jspreadsheet/install --edition pro --framework react`);
87
+ console.log(` ${c.dim('$')} npx @jspreadsheet/install -e ce -f vanilla`);
88
+ console.log(` ${c.dim('$')} npx @jspreadsheet/install --edition pro --email dev@co.com`);
89
+ console.log('');
90
+ }
91
+
92
+ // ─── Display Helpers ─────────────────────────────────────────────────
93
+
94
+ function printBanner() {
95
+ console.log('');
96
+ console.log(` ${c.cyan('┌')} ${c.bold('Jspreadsheet Installer')}`);
97
+ console.log(` ${c.cyan('│')}`);
98
+ console.log(` ${c.cyan('│')} The JavaScript data grid for building`);
99
+ console.log(` ${c.cyan('│')} enterprise-grade spreadsheet applications.`);
100
+ console.log(` ${c.cyan('└')}`);
101
+ console.log('');
102
+ }
103
+
104
+ function printFeatureComparison() {
105
+ const W = 52;
106
+ const b = c.gray('│');
107
+ const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
108
+ const row = (content) => {
109
+ const visible = strip(content).length;
110
+ const pad = Math.max(0, W - visible);
111
+ return ` ${b}${content}${' '.repeat(pad)}${b}`;
112
+ };
113
+
114
+ const lines = [
115
+ c.gray(` ┌${''.repeat(W)}┐`),
116
+ row(''),
117
+ row(` ${c.bold('Pro')} ${c.dim('— Full-featured commercial spreadsheet')}`),
118
+ row(` ${c.green('✓')} 500+ Excel-compatible formulas`),
119
+ row(` ${c.green('✓')} XLSX, CSV & clipboard import/export`),
120
+ row(` ${c.green('')} AI integration & real-time collaboration`),
121
+ row(` ${c.green('✓')} Toolbar, context menu & 30+ plugins`),
122
+ row(` ${c.green('✓')} Persistence, history & cross-sheet refs`),
123
+ row(` ${c.green('✓')} Priority support & regular updates`),
124
+ row(` ${c.dim('Free on localhost · 30-day free trial')}`),
125
+ row(''),
126
+ row(` ${c.bold('CE')} ${c.dim('— Community Edition (MIT license)')}`),
127
+ row(` ${c.green('✓')} Core spreadsheet rendering & editing`),
128
+ row(` ${c.green('✓')} Basic formulas (SUM, AVG, etc.)`),
129
+ row(` ${c.green('✓')} CSV import/export`),
130
+ row(` ${c.green('✓')} Fully open-source, no watermark`),
131
+ row(` ${c.dim('Community support via GitHub issues')}`),
132
+ row(''),
133
+ c.gray(` └${'─'.repeat(W)}┘`),
134
+ ];
135
+
136
+ for (const line of lines) {
137
+ console.log(line);
138
+ }
139
+ console.log('');
140
+ }
141
+
142
+ function printSummary(edition, framework, license, dryRun) {
143
+ const isPro = edition === 'pro';
144
+ const mainPkg = isPro ? 'jspreadsheet' : 'jspreadsheet-ce';
145
+
146
+ console.log('');
147
+ const label = dryRun ? 'ready to install' : 'installed';
148
+ console.log(` ${c.green('◇')} ${isPro ? 'Jspreadsheet Pro' : 'Jspreadsheet CE'} ${label}`);
149
+ console.log(` ${c.dim('│')}`);
150
+ console.log(` ${c.dim('│')} Edition: ${c.bold(isPro ? 'Pro' : 'Community (MIT)')}`);
151
+
152
+ if (isPro) {
153
+ if (license) {
154
+ console.log(` ${c.dim('│')} License: ${c.green(license)}`);
155
+ } else {
156
+ console.log(` ${c.dim('│')} Mode: ${c.dim('evaluation free on localhost')}`);
157
+ }
158
+ }
159
+
160
+ console.log(` ${c.dim('│')}`);
161
+ console.log(` ${c.dim('')} ${c.bold('Getting started:')}`);
162
+ console.log(` ${c.dim('')}`);
163
+
164
+ if (isPro) {
165
+ if (framework === 'react') {
166
+ console.log(` ${c.dim('│')} ${c.cyan("import { Spreadsheet, Worksheet } from '@jspreadsheet/react';")}`);
167
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
168
+ console.log(` ${c.dim('│')}`);
169
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet.setLicense('${license || 'evaluation'}');`)}`);
170
+ } else if (framework === 'vue') {
171
+ console.log(` ${c.dim('│')} ${c.cyan("import { Spreadsheet, Worksheet } from '@jspreadsheet/vue';")}`);
172
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
173
+ console.log(` ${c.dim('│')}`);
174
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet.setLicense('${license || 'evaluation'}');`)}`);
175
+ } else if (framework === 'angular') {
176
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
177
+ console.log(` ${c.dim('│')}`);
178
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet.setLicense('${license || 'evaluation'}');`)}`);
179
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet(divRef, { worksheets: [{ minDimensions: [10, 10] }] });`)}`);
180
+ console.log(` ${c.dim('│')}`);
181
+ console.log(` ${c.dim('│')} ${c.dim('Use CUSTOM_ELEMENTS_SCHEMA or call jspreadsheet in ngAfterViewInit')}`);
182
+ } else {
183
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
184
+ console.log(` ${c.dim('│')}`);
185
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet.setLicense('${license || 'evaluation'}');`)}`);
186
+ }
187
+ } else {
188
+ if (framework === 'react') {
189
+ console.log(` ${c.dim('│')} ${c.cyan("import { Spreadsheet, Worksheet } from '@jspreadsheet-ce/react';")}`);
190
+ } else if (framework === 'vue') {
191
+ console.log(` ${c.dim('│')} ${c.cyan("import { Spreadsheet, Worksheet } from '@jspreadsheet-ce/vue';")}`);
192
+ } else if (framework === 'angular') {
193
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
194
+ console.log(` ${c.dim('│')}`);
195
+ console.log(` ${c.dim('│')} ${c.cyan(`jspreadsheet(divRef, { worksheets: [{ minDimensions: [10, 10] }] });`)}`);
196
+ console.log(` ${c.dim('')}`);
197
+ console.log(` ${c.dim('│')} ${c.dim('Use CUSTOM_ELEMENTS_SCHEMA or call jspreadsheet in ngAfterViewInit')}`);
198
+ } else {
199
+ console.log(` ${c.dim('│')} ${c.cyan(`import jspreadsheet from '${mainPkg}';`)}`);
200
+ }
201
+ }
202
+
203
+ console.log(` ${c.dim('│')}`);
204
+ console.log(` ${c.dim('│')} ${c.bold('Next steps:')}`);
205
+ console.log(` ${c.dim('│')} → Start building: ${c.blue('npm run dev')}`);
206
+
207
+ if (isPro) {
208
+ console.log(` ${c.dim('│')} → Docs: ${c.blue('https://jspreadsheet.com/docs')}`);
209
+ if (!license) {
210
+ console.log(` ${c.dim('│')} → Get a license: ${c.blue('https://jspreadsheet.com/me')}`);
211
+ }
212
+ console.log(` ${c.dim('│')} Pricing: ${c.blue('https://jspreadsheet.com/pricing')}`);
213
+ } else {
214
+ console.log(` ${c.dim('│')} → Docs: ${c.blue('https://bossanova.uk/jspreadsheet/docs')}`);
215
+ console.log(` ${c.dim('│')}`);
216
+ console.log(` ${c.dim('│')} Need advanced formulas, import/export, or more?`);
217
+ console.log(` ${c.dim('│')} Try ${c.bold('Jspreadsheet Pro')}: ${c.blue('https://jspreadsheet.com')}`);
218
+ }
219
+
220
+ console.log(` ${c.dim('│')}`);
221
+ console.log(` ${c.dim('└')} Happy building!`);
222
+ console.log('');
223
+ }
224
+
225
+ // ─── Utilities ───────────────────────────────────────────────────────
226
+
227
+ function detectPackageManager() {
228
+ const cwd = process.cwd();
229
+ if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn';
230
+ if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
231
+ if (fs.existsSync(path.join(cwd, 'bun.lockb'))) return 'bun';
232
+ return 'npm';
233
+ }
234
+
235
+ function getInstallCommand(pkg) {
236
+ const pm = detectPackageManager();
237
+ if (pm === 'yarn') return `yarn add ${pkg}`;
238
+ if (pm === 'pnpm') return `pnpm add ${pkg}`;
239
+ if (pm === 'bun') return `bun add ${pkg}`;
240
+ return `npm install ${pkg}`;
241
+ }
242
+
243
+ function registerEmail(email) {
244
+ return new Promise((resolve) => {
245
+ const data = JSON.stringify({ email });
246
+
247
+ const options = {
248
+ hostname: 'jspreadsheet.com',
249
+ port: 443,
250
+ path: '/me/register',
251
+ method: 'POST',
252
+ headers: {
253
+ 'Content-Type': 'application/json',
254
+ 'Content-Length': Buffer.byteLength(data),
255
+ },
256
+ };
257
+
258
+ const req = https.request(options, (res) => {
259
+ let body = '';
260
+ res.on('data', (chunk) => body += chunk);
261
+ res.on('end', () => {
262
+ try {
263
+ const result = JSON.parse(body);
264
+ resolve(result.license || null);
265
+ } catch (e) {
266
+ resolve(null);
267
+ }
268
+ });
269
+ });
270
+
271
+ req.on('error', () => resolve(null));
272
+ req.write(data);
273
+ req.end();
274
+ });
275
+ }
276
+
277
+ // ─── Main ────────────────────────────────────────────────────────────
278
+
279
+ async function main() {
280
+ const flags = parseArgs();
281
+
282
+ // Version
283
+ if (flags.version) {
284
+ const { version } = require('./package.json');
285
+ console.log(version);
286
+ process.exit(0);
287
+ }
288
+
289
+ // Help
290
+ if (flags.help) {
291
+ printHelp();
292
+ process.exit(0);
293
+ }
294
+
295
+ // Validate flags
296
+ if (flags.edition && !VALID_EDITIONS.includes(flags.edition)) {
297
+ console.error(`\n ${c.red('')} Invalid edition: ${flags.edition}`);
298
+ console.error(` ${c.dim('Valid options: pro, ce')}\n`);
299
+ process.exit(1);
300
+ }
301
+ if (flags.framework && !VALID_FRAMEWORKS.includes(flags.framework)) {
302
+ console.error(`\n ${c.red('')} Invalid framework: ${flags.framework}`);
303
+ console.error(` ${c.dim('Valid options: react, vue, angular, vanilla')}\n`);
304
+ process.exit(1);
305
+ }
306
+
307
+ const isInteractive = process.stdin.isTTY && !process.env.CI;
308
+
309
+ // Non-interactive with missing required flags
310
+ if (!isInteractive && !flags.edition) {
311
+ console.error(`\n ${c.red('✗')} --edition is required in non-interactive mode`);
312
+ console.error(` ${c.dim('Run with --help for usage info')}\n`);
313
+ process.exit(1);
314
+ }
315
+
316
+ // ── Banner ──
317
+ printBanner();
318
+
319
+ // ── Step 1: Edition ──
320
+ let edition = flags.edition;
321
+ if (!edition) {
322
+ printFeatureComparison();
323
+ edition = await select({
324
+ message: 'Which edition would you like to install?',
325
+ choices: [
326
+ {
327
+ name: `Pro ${c.dim('— Commercial, full-featured')}`,
328
+ value: 'pro',
329
+ short: 'Pro',
330
+ },
331
+ {
332
+ name: `CE ${c.dim('— Community Edition, MIT license')}`,
333
+ value: 'ce',
334
+ short: 'CE',
335
+ },
336
+ ],
337
+ });
338
+ }
339
+
340
+ // ── Step 2: Framework ──
341
+ let framework = flags.framework;
342
+ if (!framework) {
343
+ if (!isInteractive) {
344
+ framework = 'vanilla';
345
+ console.log(` ${c.dim('No --framework specified, defaulting to vanilla')}`);
346
+ } else {
347
+ console.log('');
348
+ framework = await select({
349
+ message: 'Select your framework',
350
+ choices: [
351
+ { name: 'React', value: 'react' },
352
+ { name: 'Vue', value: 'vue' },
353
+ { name: 'Angular', value: 'angular' },
354
+ { name: 'Vanilla JavaScript', value: 'vanilla' },
355
+ new Separator(),
356
+ { name: `Other ${c.dim('(vanilla JS package)')}`, value: 'vanilla' },
357
+ ],
358
+ });
359
+ }
360
+ }
361
+
362
+ const pkg = PACKAGES[edition][framework];
363
+
364
+ // ── Step 3: Registration (Pro only) ──
365
+ let license = null;
366
+
367
+ if (edition === 'pro' && !flags.skipRegistration) {
368
+ if (flags.email) {
369
+ // Non-interactive registration
370
+ console.log(` ${c.dim('Registering')} ${flags.email}${c.dim('...')}`);
371
+ license = await registerEmail(flags.email);
372
+ if (license) {
373
+ console.log(` ${c.green('✓')} License key generated`);
374
+ } else {
375
+ console.log(` ${c.yellow('!')} Could not register. You can register later at:`);
376
+ console.log(` ${c.blue('https://jspreadsheet.com/me')}`);
377
+ }
378
+ } else if (isInteractive) {
379
+ console.log('');
380
+ const support = await select({
381
+ message: 'Get a free 30-day trial license for production deployment and support?',
382
+ choices: [
383
+ {
384
+ name: `Yes, register with my email`,
385
+ value: 'register',
386
+ description: 'Get a license key for deployment + priority support',
387
+ },
388
+ {
389
+ name: `No thanks, continue in evaluation mode`,
390
+ value: 'skip',
391
+ description: 'Free on localhost — register anytime later for production',
392
+ },
393
+ ],
394
+ });
395
+
396
+ if (support === 'register') {
397
+ console.log('');
398
+ const email = await input({
399
+ message: 'Your email',
400
+ required: true,
401
+ validate: (value) => {
402
+ if (!value) return 'Email is required';
403
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
404
+ return 'Please enter a valid email address';
405
+ }
406
+ return true;
407
+ },
408
+ });
409
+
410
+ console.log(`\n ${c.dim('Registering...')}`);
411
+ license = await registerEmail(email);
412
+
413
+ if (license) {
414
+ console.log(` ${c.green('✓')} Done! Your license key has been generated.`);
415
+ } else {
416
+ console.log(` ${c.yellow('!')} Could not register at this time.`);
417
+ console.log(` ${c.dim('You can register later at:')} ${c.blue('https://jspreadsheet.com/me')}`);
418
+ console.log(` ${c.dim('Continuing with evaluation mode.')}`);
419
+ }
420
+ }
421
+ }
422
+ }
423
+
424
+ // ── Step 4: Install ──
425
+ const cmd = getInstallCommand(pkg);
426
+
427
+ console.log('');
428
+ if (flags.dryRun) {
429
+ console.log(` ${c.yellow('●')} Dry run — would install ${c.bold(pkg)}`);
430
+ console.log(` ${c.dim('$')} ${c.dim(cmd)}`);
431
+ } else {
432
+ console.log(` ${c.cyan('●')} Installing ${c.bold(pkg)}...`);
433
+ console.log(` ${c.dim('$')} ${c.dim(cmd)}`);
434
+ console.log('');
435
+
436
+ try {
437
+ execSync(cmd, { stdio: 'inherit' });
438
+ } catch (e) {
439
+ console.error(`\n ${c.red('✗')} Installation failed. Try running manually:`);
440
+ console.error(` ${c.dim('$')} ${cmd}\n`);
441
+ process.exit(1);
442
+ }
443
+ }
444
+
445
+ // ── Step 5: Summary ──
446
+ printSummary(edition, framework, license, flags.dryRun);
447
+ }
448
+
449
+ // ─── Entry Point ─────────────────────────────────────────────────────
450
+
451
+ main().catch((err) => {
452
+ // Graceful exit on Ctrl+C
453
+ if (err.name === 'ExitPromptError') {
454
+ console.log(`\n ${c.dim('Setup cancelled.')}\n`);
455
+ process.exit(0);
456
+ }
457
+ console.error(err);
458
+ process.exit(1);
459
+ });
package/package.json CHANGED
@@ -1,21 +1,23 @@
1
- {
2
- "name": "@jspreadsheet/install",
3
- "version": "1.0.0",
4
- "description": "Interactive installer for Jspreadsheet",
5
- "bin": {
6
- "@jspreadsheet/install": "index.js",
7
- "jspreadsheet-install": "index.js"
8
- },
9
- "main": "index.js",
10
- "keywords": [
11
- "jspreadsheet",
12
- "spreadsheet",
13
- "installer",
14
- "cli"
15
- ],
16
- "author": "Jspreadsheet",
17
- "license": "MIT",
18
- "dependencies": {
19
- "prompts": "^2.4.2"
20
- }
21
- }
1
+ {
2
+ "name": "@jspreadsheet/install",
3
+ "version": "1.1.0",
4
+ "description": "Interactive installer for Jspreadsheet",
5
+ "bin": {
6
+ "@jspreadsheet/install": "index.js",
7
+ "jspreadsheet-install": "index.js"
8
+ },
9
+ "main": "index.js",
10
+ "keywords": [
11
+ "jspreadsheet",
12
+ "spreadsheet",
13
+ "installer",
14
+ "cli"
15
+ ],
16
+ "author": "Jspreadsheet",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@inquirer/prompts": "^7.10.1",
20
+ "@jspreadsheet-ce/react": "^5.0.2",
21
+ "@jspreadsheet/vue": "^12.4.2"
22
+ }
23
+ }