@jspreadsheet/install 1.0.1 → 1.2.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 +461 -326
  2. package/package.json +21 -21
package/index.js CHANGED
@@ -1,326 +1,461 @@
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: 'Which edition would you like to install?',
116
- choices: [
117
- {
118
- title: `Pro \x1b[0m\x1b[90m\u2014 Full-featured commercial spreadsheet\x1b[0m\n` +
119
- ` \x1b[32m\u2713\x1b[0m\x1b[37m 500+ Excel-compatible formulas\x1b[0m\n` +
120
- ` \x1b[32m\u2713\x1b[0m\x1b[37m XLSX, CSV & clipboard import/export\x1b[0m\n` +
121
- ` \x1b[32m\u2713\x1b[0m\x1b[37m AI integration & real-time collaboration\x1b[0m\n` +
122
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Toolbar, context menu & 30+ plugins\x1b[0m\n` +
123
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Persistence, history & cross-sheet references\x1b[0m\n` +
124
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Priority support & regular updates\x1b[0m\n` +
125
- ` \x1b[90mFree on localhost or 30-day free trial for production\x1b[0m`,
126
- value: 'pro',
127
- },
128
- {
129
- title: `CE \x1b[0m\x1b[90m\u2014 Community Edition (MIT license)\x1b[0m\n` +
130
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Core spreadsheet rendering & editing\x1b[0m\n` +
131
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Basic formulas (SUM, AVG, etc.)\x1b[0m\n` +
132
- ` \x1b[32m\u2713\x1b[0m\x1b[37m CSV import/export\x1b[0m\n` +
133
- ` \x1b[32m\u2713\x1b[0m\x1b[37m Fully open-source, no watermark\x1b[0m\n` +
134
- ` \x1b[90mCommunity support via GitHub issues\x1b[0m`,
135
- value: 'ce',
136
- },
137
- ],
138
- });
139
-
140
- if (!distribution) {
141
- console.log('\nSetup cancelled.');
142
- process.exit(0);
143
- }
144
-
145
- console.log('');
146
-
147
- // Step 2: Choose framework
148
- const { framework } = await prompts({
149
- type: 'select',
150
- name: 'framework',
151
- message: 'Select your framework',
152
- choices: [
153
- { title: 'React', value: 'react' },
154
- { title: 'Vue', value: 'vue' },
155
- { title: 'Angular', value: 'angular' },
156
- { title: 'Vanilla JavaScript', value: 'vanilla' },
157
- { title: 'Other', value: 'vanilla' },
158
- ],
159
- });
160
-
161
- if (!framework) {
162
- console.log('\nSetup cancelled.');
163
- process.exit(0);
164
- }
165
-
166
- const pkg = PACKAGES[distribution][framework];
167
-
168
- // Step 3: Pro-specific registration flow
169
- let license = null;
170
-
171
- if (distribution === 'pro') {
172
- console.log('');
173
- const { support } = await prompts({
174
- type: 'select',
175
- name: 'support',
176
- message: 'Get free dedicated technical support for 30 days?',
177
- choices: [
178
- {
179
- title: 'Yes, register with my email',
180
- description: 'Receive a license key and priority support from the Jspreadsheet team.',
181
- value: 'register',
182
- },
183
- {
184
- title: 'No thanks, start evaluation without registration',
185
- description: 'You can register anytime later.',
186
- value: 'skip',
187
- },
188
- ],
189
- });
190
-
191
- if (!support) {
192
- console.log('\nSetup cancelled.');
193
- process.exit(0);
194
- }
195
-
196
- if (support === 'register') {
197
- const { email } = await prompts({
198
- type: 'text',
199
- name: 'email',
200
- message: 'Your email',
201
- validate: (value) => {
202
- if (!value) {
203
- return 'Email is required';
204
- }
205
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
206
- return 'Please enter a valid email address';
207
- }
208
- return true;
209
- },
210
- });
211
-
212
- if (!email) {
213
- console.log('\nSetup cancelled.');
214
- process.exit(0);
215
- }
216
-
217
- console.log('\n Registering...');
218
- license = await registerEmail(email);
219
-
220
- if (license) {
221
- console.log(' Done! Your license key has been generated.\n');
222
- } else {
223
- console.log(' Could not register at this time. You can register later at:');
224
- console.log(' https://jspreadsheet.com/me\n');
225
- console.log(' Continuing with evaluation mode.\n');
226
- }
227
- }
228
- }
229
-
230
- // Step 4: Install the package
231
- const cmd = getInstallCommand(pkg);
232
- console.log('');
233
- console.log(` Installing ${pkg}...`);
234
- console.log(` > ${cmd}`);
235
- console.log('');
236
-
237
- try {
238
- execSync(cmd, { stdio: 'inherit' });
239
- } catch (e) {
240
- console.error(`\n Installation failed. Try running manually:`);
241
- console.error(` ${cmd}\n`);
242
- process.exit(1);
243
- }
244
-
245
- // Step 5: Show post-install summary
246
- const isPro = distribution === 'pro';
247
- const mainPkg = isPro ? 'jspreadsheet' : 'jspreadsheet-ce';
248
-
249
- console.log('');
250
- console.log('');
251
-
252
- if (isPro) {
253
- console.log(` \x1b[32m\u2713\x1b[0m Jspreadsheet Pro installed`);
254
- console.log('');
255
- console.log(` Edition: Pro`);
256
- if (license) {
257
- console.log(` License: ${license}`);
258
- } else {
259
- console.log(` Mode: evaluation (free on localhost)`);
260
- console.log(` Time limit: none on localhost`);
261
- }
262
- console.log('');
263
- console.log('');
264
- console.log(' Getting started:');
265
- console.log('');
266
-
267
- if (framework === 'react') {
268
- console.log(` import { Spreadsheet, Worksheet } from '@jspreadsheet/react';`);
269
- console.log(` import jspreadsheet from '${mainPkg}';`);
270
- console.log('');
271
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
272
- } else if (framework === 'vue') {
273
- console.log(` import { Spreadsheet, Worksheet } from '@jspreadsheet/vue';`);
274
- console.log(` import jspreadsheet from '${mainPkg}';`);
275
- console.log('');
276
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
277
- } else {
278
- console.log(` import jspreadsheet from '${mainPkg}';`);
279
- console.log('');
280
- console.log(` jspreadsheet.setLicense('${license || 'evaluation'}');`);
281
- }
282
-
283
- console.log('');
284
- console.log('');
285
- console.log(' Next steps:');
286
- console.log(` \u2192 Start building: npm run dev`);
287
- console.log(` \u2192 Docs: https://jspreadsheet.com/docs`);
288
- if (!license) {
289
- console.log(` \u2192 Get a license: https://jspreadsheet.com/me`);
290
- }
291
- console.log(` \u2192 Pricing: https://jspreadsheet.com/pricing`);
292
- } else {
293
- console.log(` \x1b[32m\u2713\x1b[0m Jspreadsheet CE installed`);
294
- console.log('');
295
- console.log(` Edition: Community (MIT license)`);
296
- console.log('');
297
- console.log('');
298
- console.log(' Getting started:');
299
- console.log('');
300
-
301
- if (framework === 'react') {
302
- console.log(` import { Spreadsheet, Worksheet } from '@jspreadsheet-ce/react';`);
303
- } else if (framework === 'vue') {
304
- console.log(` import { Spreadsheet, Worksheet } from '@jspreadsheet-ce/vue';`);
305
- } else {
306
- console.log(` import jspreadsheet from '${mainPkg}';`);
307
- }
308
-
309
- console.log('');
310
- console.log('');
311
- console.log(' Next steps:');
312
- console.log(` \u2192 Start building: npm run dev`);
313
- console.log(` \u2192 Docs: https://bossanova.uk/jspreadsheet/docs`);
314
- console.log('');
315
- console.log(' Need advanced formulas, file import/export, or more features?');
316
- console.log(' Try Jspreadsheet Pro: https://jspreadsheet.com');
317
- }
318
-
319
- console.log('');
320
- console.log('');
321
- }
322
-
323
- main().catch((err) => {
324
- console.error(err);
325
- process.exit(1);
326
- });
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 payload = JSON.stringify({ email });
246
+ const { version } = require('./package.json');
247
+
248
+ const options = {
249
+ hostname: 'jspreadsheet.com',
250
+ port: 443,
251
+ path: '/register',
252
+ method: 'POST',
253
+ headers: {
254
+ 'Content-Type': 'application/json',
255
+ 'Content-Length': Buffer.byteLength(payload),
256
+ 'User-Agent': `jspreadsheet-install/${version} (node ${process.version})`,
257
+ 'Accept': 'text/plain',
258
+ },
259
+ };
260
+
261
+ const req = https.request(options, (res) => {
262
+ let body = '';
263
+ res.on('data', (chunk) => body += chunk);
264
+ res.on('end', () => {
265
+ if (res.statusCode < 200 || res.statusCode >= 300) {
266
+ return resolve(null);
267
+ }
268
+ const license = body.trim();
269
+ resolve(license || null);
270
+ });
271
+ });
272
+
273
+ req.on('error', () => resolve(null));
274
+ req.write(payload);
275
+ req.end();
276
+ });
277
+ }
278
+
279
+ // ─── Main ────────────────────────────────────────────────────────────
280
+
281
+ async function main() {
282
+ const flags = parseArgs();
283
+
284
+ // Version
285
+ if (flags.version) {
286
+ const { version } = require('./package.json');
287
+ console.log(version);
288
+ process.exit(0);
289
+ }
290
+
291
+ // Help
292
+ if (flags.help) {
293
+ printHelp();
294
+ process.exit(0);
295
+ }
296
+
297
+ // Validate flags
298
+ if (flags.edition && !VALID_EDITIONS.includes(flags.edition)) {
299
+ console.error(`\n ${c.red('')} Invalid edition: ${flags.edition}`);
300
+ console.error(` ${c.dim('Valid options: pro, ce')}\n`);
301
+ process.exit(1);
302
+ }
303
+ if (flags.framework && !VALID_FRAMEWORKS.includes(flags.framework)) {
304
+ console.error(`\n ${c.red('✗')} Invalid framework: ${flags.framework}`);
305
+ console.error(` ${c.dim('Valid options: react, vue, angular, vanilla')}\n`);
306
+ process.exit(1);
307
+ }
308
+
309
+ const isInteractive = process.stdin.isTTY && !process.env.CI;
310
+
311
+ // Non-interactive with missing required flags
312
+ if (!isInteractive && !flags.edition) {
313
+ console.error(`\n ${c.red('✗')} --edition is required in non-interactive mode`);
314
+ console.error(` ${c.dim('Run with --help for usage info')}\n`);
315
+ process.exit(1);
316
+ }
317
+
318
+ // ── Banner ──
319
+ printBanner();
320
+
321
+ // ── Step 1: Edition ──
322
+ let edition = flags.edition;
323
+ if (!edition) {
324
+ printFeatureComparison();
325
+ edition = await select({
326
+ message: 'Which edition would you like to install?',
327
+ choices: [
328
+ {
329
+ name: `Pro ${c.dim('— Commercial, full-featured')}`,
330
+ value: 'pro',
331
+ short: 'Pro',
332
+ },
333
+ {
334
+ name: `CE ${c.dim('— Community Edition, MIT license')}`,
335
+ value: 'ce',
336
+ short: 'CE',
337
+ },
338
+ ],
339
+ });
340
+ }
341
+
342
+ // ── Step 2: Framework ──
343
+ let framework = flags.framework;
344
+ if (!framework) {
345
+ if (!isInteractive) {
346
+ framework = 'vanilla';
347
+ console.log(` ${c.dim('No --framework specified, defaulting to vanilla')}`);
348
+ } else {
349
+ console.log('');
350
+ framework = await select({
351
+ message: 'Select your framework',
352
+ choices: [
353
+ { name: 'React', value: 'react' },
354
+ { name: 'Vue', value: 'vue' },
355
+ { name: 'Angular', value: 'angular' },
356
+ { name: 'Vanilla JavaScript', value: 'vanilla' },
357
+ new Separator(),
358
+ { name: `Other ${c.dim('(vanilla JS package)')}`, value: 'vanilla' },
359
+ ],
360
+ });
361
+ }
362
+ }
363
+
364
+ const pkg = PACKAGES[edition][framework];
365
+
366
+ // ── Step 3: Registration (Pro only) ──
367
+ let license = null;
368
+
369
+ if (edition === 'pro' && !flags.skipRegistration) {
370
+ if (flags.email) {
371
+ // Non-interactive registration
372
+ console.log(` ${c.dim('Registering')} ${flags.email}${c.dim('...')}`);
373
+ license = await registerEmail(flags.email);
374
+ if (license) {
375
+ console.log(` ${c.green('✓')} License key generated`);
376
+ } else {
377
+ console.log(` ${c.yellow('!')} Could not register. You can register later at:`);
378
+ console.log(` ${c.blue('https://jspreadsheet.com/me')}`);
379
+ }
380
+ } else if (isInteractive) {
381
+ console.log('');
382
+ const support = await select({
383
+ message: 'Get a free 30-day trial license for production deployment and support?',
384
+ choices: [
385
+ {
386
+ name: `Yes, register with my email`,
387
+ value: 'register',
388
+ description: 'Get a license key for deployment + priority support',
389
+ },
390
+ {
391
+ name: `No thanks, continue in evaluation mode`,
392
+ value: 'skip',
393
+ description: 'Free on localhost — register anytime later for production',
394
+ },
395
+ ],
396
+ });
397
+
398
+ if (support === 'register') {
399
+ console.log('');
400
+ const email = await input({
401
+ message: 'Your email',
402
+ required: true,
403
+ validate: (value) => {
404
+ if (!value) return 'Email is required';
405
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
406
+ return 'Please enter a valid email address';
407
+ }
408
+ return true;
409
+ },
410
+ });
411
+
412
+ console.log(`\n ${c.dim('Registering...')}`);
413
+ license = await registerEmail(email);
414
+
415
+ if (license) {
416
+ console.log(` ${c.green('✓')} Done! Your license key has been generated.`);
417
+ } else {
418
+ console.log(` ${c.yellow('!')} Could not register at this time.`);
419
+ console.log(` ${c.dim('You can register later at:')} ${c.blue('https://jspreadsheet.com/me')}`);
420
+ console.log(` ${c.dim('Continuing with evaluation mode.')}`);
421
+ }
422
+ }
423
+ }
424
+ }
425
+
426
+ // ── Step 4: Install ──
427
+ const cmd = getInstallCommand(pkg);
428
+
429
+ console.log('');
430
+ if (flags.dryRun) {
431
+ console.log(` ${c.yellow('●')} Dry run — would install ${c.bold(pkg)}`);
432
+ console.log(` ${c.dim('$')} ${c.dim(cmd)}`);
433
+ } else {
434
+ console.log(` ${c.cyan('●')} Installing ${c.bold(pkg)}...`);
435
+ console.log(` ${c.dim('$')} ${c.dim(cmd)}`);
436
+ console.log('');
437
+
438
+ try {
439
+ execSync(cmd, { stdio: 'inherit' });
440
+ } catch (e) {
441
+ console.error(`\n ${c.red('✗')} Installation failed. Try running manually:`);
442
+ console.error(` ${c.dim('$')} ${cmd}\n`);
443
+ process.exit(1);
444
+ }
445
+ }
446
+
447
+ // ── Step 5: Summary ──
448
+ printSummary(edition, framework, license, flags.dryRun);
449
+ }
450
+
451
+ // ─── Entry Point ─────────────────────────────────────────────────────
452
+
453
+ main().catch((err) => {
454
+ // Graceful exit on Ctrl+C
455
+ if (err.name === 'ExitPromptError') {
456
+ console.log(`\n ${c.dim('Setup cancelled.')}\n`);
457
+ process.exit(0);
458
+ }
459
+ console.error(err);
460
+ process.exit(1);
461
+ });
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
- {
2
- "name": "@jspreadsheet/install",
3
- "version": "1.0.1",
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.2.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
+ }
21
+ }