@reveldigital/gadgetizer 1.0.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 (3) hide show
  1. package/README.md +126 -0
  2. package/cli.js +255 -0
  3. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # 🚀 Revel Digital Gadgetizer
2
+
3
+ A command-line tool for integrating the Revel Digital client SDK into existing Angular, Vue, React, or vanilla JavaScript projects. This tool automates the setup process and generates the necessary configuration files for deploying your application as a Revel Digital gadget.
4
+
5
+ ## 📦 Installation
6
+
7
+ Install globally via npm:
8
+
9
+ ```bash
10
+ npm install -g @reveldigital/gadgetizer
11
+ ```
12
+
13
+ Or use directly with npx:
14
+
15
+ ```bash
16
+ npx @reveldigital/gadgetizer
17
+ ```
18
+
19
+ ## 🎯 Usage
20
+
21
+ Navigate to your existing project directory and run:
22
+
23
+ ```bash
24
+ gadgetizer
25
+ ```
26
+
27
+ The tool will guide you through the setup process with interactive prompts.
28
+
29
+ ### Command Options
30
+
31
+ - `--help`, `-h` - Show help message
32
+ - `--build-only` - Run only the XML build step (skips SDK installation)
33
+
34
+ ### Examples
35
+
36
+ Basic usage:
37
+ ```bash
38
+ cd my-react-app
39
+ gadgetizer
40
+ ```
41
+
42
+ Build XML only (useful for CI/CD):
43
+ ```bash
44
+ gadgetizer --build-only
45
+ ```
46
+
47
+ Show help:
48
+ ```bash
49
+ gadgetizer --help
50
+ ```
51
+
52
+ ## ⚡ What It Does
53
+
54
+ The Gadgetizer tool performs the following actions based on your project type:
55
+
56
+ ### For Angular Projects
57
+ - Installs `@reveldigital/player-client@latest`
58
+ - Runs the Angular schematic: `ng add @reveldigital/player-client@latest`
59
+
60
+ ### For React, Vue, and Vanilla JavaScript Projects
61
+ - Copies `gadget.yaml` configuration file to your project root (if not already present)
62
+ - Prompts for deployment URL configuration
63
+ - Installs the following dependencies:
64
+ - `@reveldigital/client-sdk`
65
+ - `@reveldigital/gadget-types`
66
+ - Adds a `build:gadget` script to your `package.json`
67
+ - Generates XML gadget configuration file in your build directory
68
+
69
+ ## 📁 Project Structure
70
+
71
+ After running the tool, your project will have:
72
+
73
+ - 📄 `gadget.yaml` - Gadget configuration file
74
+ - 📦 Updated `package.json` with Revel Digital dependencies
75
+ - 🛠️ `build:gadget` npm script for regenerating the gadget XML
76
+ - 🗂️ Generated XML file in your build/dist directory
77
+
78
+ ## ⚙️ Configuration
79
+
80
+ The `gadget.yaml` file contains metadata about your gadget:
81
+
82
+ ```yaml
83
+ title: My Gadget
84
+ title_url: https://mysupporturl.org
85
+ description: Describe the purpose of your gadget here
86
+ author: My Organization
87
+ background: transparent
88
+
89
+ requirements:
90
+ - reveldigital
91
+ - offline
92
+ - webfont
93
+
94
+ prefs:
95
+ - name: myStringPref
96
+ display_name: Sample string preference
97
+ datatype: string
98
+ default_value: test string
99
+ required: true
100
+ ```
101
+
102
+ Edit this file to customize your gadget's properties and user preferences.
103
+
104
+ ## 🛠️ Supported Frameworks
105
+
106
+ - **Angular** - Uses the official Revel Digital Angular schematic
107
+ - **React** - Instruments with client SDK and generates gadget configuration
108
+ - **Vue** - Instruments with client SDK and generates gadget configuration
109
+ - **Vanilla JavaScript** - Instruments with client SDK and generates gadget configuration
110
+
111
+ ## 📚 Revel Digital Client SDK
112
+
113
+ For more information about the Revel Digital client SDK, including API documentation and examples, visit:
114
+ https://www.npmjs.com/package/@reveldigital/client-sdk
115
+
116
+
117
+ ## 📜 License
118
+
119
+ ISC
120
+
121
+ ## 🤝 Support
122
+
123
+ For issues and questions:
124
+ - File an issue on this repository
125
+ - Visit the Revel Digital documentation at https://developer.reveldigital.com
126
+ - Check the client SDK documentation at https://www.npmjs.com/package/@reveldigital/client-sdk
package/cli.js ADDED
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { confirm, intro, outro, select, text } from '@clack/prompts';
4
+ import { execSync } from 'child_process';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import pc from 'picocolors';
8
+ import { getDeploymentUrl } from './utils/changeBasePath.js';
9
+ import { convertYmlToXml } from './utils/yml2xml.js';
10
+ import { dirname } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ //const __filename = fileURLToPath(import.meta.url);
14
+ //const __filename = fileURLToPath(process.cwd());
15
+ //const __dirname = path.dirname(__filename);
16
+ const __dirname = dirname(process.argv[1]);
17
+
18
+ // Parse command line arguments
19
+ const args = process.argv.slice(2);
20
+ const showHelp = args.includes('help') || args.includes('--help') || args.includes('-h');
21
+ const buildOnly = args.includes('build-only') || args.includes('--build-only');
22
+ const projectPath = process.cwd();
23
+
24
+ intro(pc.cyan('🚀 Revel Digital SDK Integrator'));
25
+
26
+ if (showHelp) {
27
+ outro(pc.cyan(`
28
+ Usage: gadgetizer [options]
29
+
30
+ Options:
31
+ --help, -h Show this help message
32
+ --build-only Run only the Gadget XML build step
33
+
34
+ Description:
35
+ Integrates the Revel Digital SDK into your project and generates gadget XML.
36
+ Supports React, Vue, Angular, and vanilla JavaScript projects.
37
+ `));
38
+ process.exit(0);
39
+ }
40
+
41
+ if (buildOnly) {
42
+ build({ projectPath: process.cwd(), __dirname });
43
+ process.exit(0);
44
+ }
45
+
46
+ const framework = await select({
47
+ message: 'Select your project framework:',
48
+ options: [
49
+ { value: 'react', label: 'React' },
50
+ { value: 'vue', label: 'Vue' },
51
+ { value: 'angular', label: 'Angular' },
52
+ { value: 'js', label: 'Vanilla JavaScript' }
53
+ ]
54
+ });
55
+
56
+ let entryFile = '';
57
+ switch (framework) {
58
+ case 'react':
59
+ entryFile = fs.existsSync(path.join(projectPath, 'src', 'App.js')) ? path.join('src', 'App.js') : '';
60
+ break;
61
+ case 'vue':
62
+ entryFile = fs.existsSync(path.join(projectPath, 'src', 'App.vue')) ? path.join('src', 'App.vue') : '';
63
+ break;
64
+ case 'angular':
65
+ entryFile = fs.existsSync(path.join(projectPath, 'src', 'main.ts')) ? path.join('src', 'main.ts') : '';
66
+ break;
67
+ case 'js':
68
+ entryFile = fs.existsSync(path.join(projectPath, 'src', 'main.js')) ? path.join('src', 'main.js') : '';
69
+ break;
70
+ }
71
+ if (!entryFile) {
72
+ outro(pc.yellow(`⚠️ Could not automatically detect the main entry file for ${framework}. Please manually instrument your app with the Revel Digital SDK.`));
73
+ }
74
+
75
+ const pkgPath = path.join(projectPath, 'package.json');
76
+ if (!fs.existsSync(pkgPath)) {
77
+ outro(pc.red('❌ No package.json found in this directory. Please run this tool from your project root.'));
78
+ process.exit(1);
79
+ }
80
+
81
+ if (framework === 'angular') { // Angular uses schematic for integration
82
+ outro(pc.cyan('📦 Installing @reveldigital/player-client and running ng add schematic...'));
83
+ try {
84
+ execSync('npm install @reveldigital/player-client@latest', { stdio: 'inherit' });
85
+ execSync('ng add @reveldigital/player-client@latest', { stdio: 'inherit' });
86
+ outro(pc.green('✅ Angular project successfully instrumented with Revel Digital Player Client.'));
87
+ process.exit(0);
88
+ } catch (err) {
89
+ outro(pc.red('❌ Error instrumenting Angular project. Please check your Angular CLI and npm setup.'));
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+
95
+ // All other frameworks utilize SDK package installation
96
+ // Copy gadget.yaml to project root
97
+ const sourceYaml = path.join(__dirname, 'assets', 'gadget.yaml');
98
+ const destYaml = path.join(projectPath, 'gadget.yaml');
99
+ if (!fs.existsSync(destYaml)) {
100
+ try {
101
+ fs.copyFileSync(sourceYaml, destYaml);
102
+ outro(pc.green('📄 gadget.yaml copied to project root.'));
103
+ } catch (err) {
104
+ outro(pc.red('❌ Failed to copy gadget.yaml to project root. Please copy it manually.'));
105
+ }
106
+ } else {
107
+ outro(pc.yellow('⚠️ gadget.yaml already exists in project root. Skipping copy.'));
108
+ }
109
+
110
+
111
+ // Prompt for deployment URL
112
+ let defaultDeploymentUrl = '';
113
+ try {
114
+ defaultDeploymentUrl = await getDeploymentUrl(pkgPath);
115
+ outro(pc.cyan(`🌎 Configuring gadget deployment URL based on Git: ${defaultDeploymentUrl}`));
116
+ } catch (e) {
117
+ defaultDeploymentUrl = '';
118
+ outro(pc.yellow(`⚠️ Warning: Could not get deployment URL automatically.`));
119
+ }
120
+ const deploymentUrl = await text({
121
+ message: 'Enter the deployment URL:',
122
+ initialValue: defaultDeploymentUrl
123
+ });
124
+
125
+ // Prompt for SDK version
126
+ const sdkVersion = await select({
127
+ message: 'Select Revel Digital SDK version to install:',
128
+ options: [
129
+ { value: 'latest', label: 'Latest' },
130
+ { value: 'custom', label: 'Custom version' }
131
+ ]
132
+ });
133
+
134
+ let customVersion = '';
135
+ if (sdkVersion === 'custom') {
136
+ customVersion = await text({
137
+ message: 'Enter the custom SDK version:'
138
+ });
139
+ }
140
+
141
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
142
+
143
+ const shouldInstall = await confirm({
144
+ message: `Install Revel Digital SDK (${sdkVersion === 'custom' ? customVersion : sdkVersion})?`
145
+ });
146
+ if (shouldInstall) {
147
+ // Add SDK to package.json dependencies
148
+ pkg.dependencies = pkg.dependencies || {};
149
+ const gadgetTypesPkgName = '@reveldigital/gadget-types';
150
+ pkg.dependencies[gadgetTypesPkgName] = 'latest';
151
+ const sdkPkgName = '@reveldigital/client-sdk';
152
+ const versionToInstall = sdkVersion === 'custom' ? customVersion : sdkVersion;
153
+ pkg.dependencies[sdkPkgName] = versionToInstall === 'latest' ? 'latest' : versionToInstall;
154
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
155
+
156
+ outro(pc.cyan(`🎉 Revel Digital SDK (${versionToInstall}) added to dependencies.`));
157
+
158
+ try {
159
+ execSync('npm install', { stdio: 'inherit' });
160
+ outro(pc.green('📦 npm install completed successfully.'));
161
+ } catch (err) {
162
+ outro(pc.red('❌ npm install failed. Please run it manually.'));
163
+ }
164
+ }
165
+
166
+ // For Vue or Vite projects, set the base path in their respective configs
167
+ const viteConfigJsPath = path.join(projectPath, 'vite.config.js');
168
+ const viteConfigTsPath = path.join(projectPath, 'vite.config.ts');
169
+ const vueConfigPath = path.join(projectPath, 'vue.config.js');
170
+
171
+ let viteConfigPath = '';
172
+ if (fs.existsSync(viteConfigJsPath)) {
173
+ viteConfigPath = viteConfigJsPath;
174
+ } else if (fs.existsSync(viteConfigTsPath)) {
175
+ viteConfigPath = viteConfigTsPath;
176
+ }
177
+
178
+ if (viteConfigPath) {
179
+ try {
180
+ let content = fs.readFileSync(viteConfigPath, 'utf8');
181
+ if (/\bbase\s*:/.test(content)) {
182
+ content = content.replace(/(\bbase\s*:\s*['"])[^'"]*(['"])/, `$1${deploymentUrl}$2`);
183
+ outro(pc.yellow(`🔧 Updated "base" in ${path.basename(viteConfigPath)}.`));
184
+ } else if (content.includes('defineConfig({')) {
185
+ content = content.replace(/(defineConfig\({)/, `$1\n base: '${deploymentUrl}',`);
186
+ outro(pc.green(`🔧 Added "base" to ${path.basename(viteConfigPath)}.`));
187
+ }
188
+ fs.writeFileSync(viteConfigPath, content);
189
+ } catch (err) {
190
+ outro(pc.red(`❌ Failed to update ${path.basename(viteConfigPath)}. Please set the "base" property manually.`));
191
+ }
192
+ } else if (fs.existsSync(vueConfigPath)) {
193
+ try {
194
+ let content = fs.readFileSync(vueConfigPath, 'utf8');
195
+ if (/\bpublicPath\s*:/.test(content)) {
196
+ content = content.replace(/(\bpublicPath\s*:\s*['"])[^'"]*(['"])/, `$1${deploymentUrl}$2`);
197
+ outro(pc.yellow('🔧 Updated "publicPath" in vue.config.js.'));
198
+ } else if (content.includes('module.exports = {')) {
199
+ content = content.replace(/(module.exports = {)/, `$1\n publicPath: '${deploymentUrl}',`);
200
+ outro(pc.green('🔧 Added "publicPath" to vue.config.js.'));
201
+ }
202
+ fs.writeFileSync(vueConfigPath, content);
203
+ } catch (err) {
204
+ outro(pc.red('❌ Failed to update vue.config.js. Please set the "publicPath" property manually.'));
205
+ }
206
+ }
207
+
208
+ // For plain JS projects, check if Parcel is used and configure publicUrl
209
+ const isParcel = (pkg.dependencies && pkg.dependencies.parcel) || (pkg.devDependencies && pkg.devDependencies.parcel);
210
+ if (isParcel) {
211
+ pkg.targets = pkg.targets || {};
212
+ pkg.targets.default = pkg.targets.default || {};
213
+ pkg.targets.default.publicUrl = deploymentUrl;
214
+ outro(pc.green('🔧 Set "targets.default.publicUrl" in package.json for Parcel.'));
215
+ }
216
+
217
+ pkg.scripts = pkg.scripts || {};
218
+ pkg.scripts['build:gadget'] = 'npm run build && gadgetizer --build-only';
219
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
220
+ outro(pc.green('🔧 Added "build:gadget" script to package.json.'));
221
+
222
+
223
+ build({ projectPath, __dirname });
224
+
225
+
226
+ function build({ projectPath, __dirname }) {
227
+ const pkgPath = path.join(projectPath, 'package.json');
228
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
229
+ const destYaml = path.join(projectPath, 'gadget.yaml');
230
+ const htmlCandidates = [
231
+ path.join(projectPath, 'dist', 'index.html'),
232
+ path.join(projectPath, 'public', 'index.html'),
233
+ ];
234
+ let foundHtml = '';
235
+ for (const candidate of htmlCandidates) {
236
+ if (fs.existsSync(candidate)) {
237
+ foundHtml = candidate;
238
+ break;
239
+ }
240
+ }
241
+ if (!foundHtml) {
242
+ outro(pc.red('❌ index.html not found in public/, src/, or project root. Please add it and re-run the tool.'));
243
+ process.exit(1);
244
+ }
245
+ const distDir = path.join(path.dirname(foundHtml));
246
+ //const yml2xmlPath = path.join(__dirname, 'utils', 'yml2xml.js');
247
+ try {
248
+ const outputXmlName = `${pkg.name}.xml`;
249
+ convertYmlToXml(destYaml, foundHtml, path.join(distDir, outputXmlName), pkg.version);
250
+ //execSync(`node "${yml2xmlPath}" "${destYaml}" "${foundHtml}" "${path.join(distDir, outputXmlName)}" "${pkg.version}"`, { encoding: 'utf8' });
251
+ outro(pc.green(`✅ Gadget XML file "${outputXmlName}" generated in dist folder.`));
252
+ } catch (err) {
253
+ outro(pc.red('❌ Failed to generate XML file. Please check yml2xml.js and your input files.'));
254
+ }
255
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@reveldigital/gadgetizer",
3
+ "version": "1.0.0",
4
+ "description": "Add Revel Digital SDK bindings to your project and configure for deployment",
5
+ "main": "cli.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "gadgetizer": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "/dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "parcel build cli.js --target node && shx cp -r assets dist/",
15
+ "start": "node ./dist/main.js",
16
+ "link": "npm link",
17
+ "test": "echo \"Error: no test specified\" && exit 1"
18
+ },
19
+ "targets": {
20
+ "node": {
21
+ "context": "node",
22
+ "includeNodeModules": true,
23
+ "outputFormat": "esmodule",
24
+ "engines": {
25
+ "node": ">=20"
26
+ }
27
+ }
28
+ },
29
+ "author": "Revel Digital Worldwide Operations",
30
+ "license": "ISC",
31
+ "dependencies": {
32
+ "@types/node": "^24.3.1",
33
+ "@clack/prompts": "^0.11.0",
34
+ "git-remote-origin-url": "4.0.0",
35
+ "js-yaml": "4.1.0",
36
+ "node-html-parser": "7.0.1",
37
+ "picocolors": "^1.0.0",
38
+ "xmlbuilder2": "3.1.1"
39
+ },
40
+ "devDependencies": {
41
+ "typescript": "^5.9.2",
42
+ "parcel": "^2.15.4"
43
+ },
44
+ "release": {
45
+ "branches": [
46
+ "main"
47
+ ],
48
+ "plugins": [
49
+ "@semantic-release/commit-analyzer",
50
+ "@semantic-release/release-notes-generator",
51
+ "@semantic-release/npm",
52
+ "@semantic-release/github"
53
+ ]
54
+ }
55
+ }