@tixyel/cli 1.0.0 → 2.0.1

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 CHANGED
@@ -1,31 +1,31 @@
1
- # @tixyel/cli
2
-
3
- CLI tool for Tixyel widgets.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g @tixyel/cli
9
- ```
10
-
11
- ## Usage
12
-
13
- ```bash
14
- tixyel --help
15
- ```
16
-
17
- ## Development
18
-
19
- ```bash
20
- npm run dev
21
- ```
22
-
23
- ## Build
24
-
25
- ```bash
26
- npm run build
27
- ```
28
-
29
- ## License
30
-
31
- Apache-2.0
1
+ # @tixyel/cli
2
+
3
+ CLI tool for Tixyel widgets.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @tixyel/cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ tixyel --help
15
+ ```
16
+
17
+ ## Development
18
+
19
+ ```bash
20
+ npm run dev
21
+ ```
22
+
23
+ ## Build
24
+
25
+ ```bash
26
+ npm run build
27
+ ```
28
+
29
+ ## License
30
+
31
+ Apache-2.0
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export type { TixyelCliConfig, ScaffoldItem, ScaffoldFile, ScaffoldFolder } from './types/tixyel-cli-config.js';
2
+ export * from './workspace.js';
package/dist/index.js CHANGED
@@ -1,39 +1,194 @@
1
1
  #!/usr/bin/env node
2
- import { Command as CommanderCommand } from 'commander';
3
- import { fileURLToPath } from 'url';
4
- import { dirname, join } from 'path';
5
- import { readdirSync } from 'fs';
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
- const program = new CommanderCommand();
9
- program.name('tixyel').description('CLI tool for Tixyel widgets').version('1.0.0');
10
- /**
11
- * Auto-discover and load command classes from the commands directory
12
- */
13
- async function loadCommands() {
14
- const commandsDir = join(__dirname, 'commands');
15
- const files = readdirSync(commandsDir).filter((file) => file.endsWith('.js') && file !== 'base.js');
16
- for (const file of files) {
17
- try {
18
- const modulePath = join(commandsDir, file);
19
- const module = await import(`file://${modulePath}`);
20
- // Look for class exports
21
- for (const key in module) {
22
- const exported = module[key];
23
- // Check if it's a class that extends Command
24
- if (typeof exported === 'function' && exported.prototype && exported.prototype.register) {
25
- const CommandClass = exported;
26
- const instance = new CommandClass();
27
- instance.register(program);
28
- break;
29
- }
2
+ import { build, createWidget, findWidgets, getNextWidgetNumber } from './widget.js';
3
+ import { loadWorkspace, validateWorkspace } from './workspace.js';
4
+ import { workspace_config } from './templates/workspace.js';
5
+ import { Command as Commander } from 'commander';
6
+ import { basename, join, resolve } from 'path';
7
+ import { writeFile } from 'fs/promises';
8
+ import { createRequire } from 'module';
9
+ import inquirer from 'inquirer';
10
+ const program = new Commander();
11
+ program
12
+ .name('tixyel')
13
+ .description('CLI tool for streamelements widgets')
14
+ .version((() => {
15
+ try {
16
+ const require = createRequire(import.meta.url);
17
+ const { version } = require('../package.json');
18
+ return version ?? 'dev';
19
+ }
20
+ catch {
21
+ return process.env.TIXYEL_VERSION ?? 'dev';
22
+ }
23
+ })());
24
+ program
25
+ .command('init')
26
+ .aliases(['initialize', 'i', 'setup', 'start'])
27
+ .description('Initialize a new widget workspace.')
28
+ .action(async () => {
29
+ const root = process.cwd();
30
+ const config = resolve(root, 'tixyel.config.ts');
31
+ console.log('🚀 Initializing new workspace...');
32
+ console.log(`📁 Workspace root: ${root}`);
33
+ const content = workspace_config;
34
+ try {
35
+ await writeFile(config, content, 'utf-8');
36
+ console.log(`✅ Created tixyel.config.ts`);
37
+ console.log(' Edit it to customize your workspace settings.');
38
+ console.log('\n🎉 All set! Start building your widgets!');
39
+ console.log(' - Use "tixyel generate" to create new widgets.');
40
+ console.log(' - Use "tixyel build" to build your widgets for publish.');
41
+ }
42
+ catch (error) {
43
+ throw error;
44
+ }
45
+ });
46
+ program
47
+ .command('generate [path] [name] [description] [tags]')
48
+ .aliases(['new', 'g', 'create', 'widget'])
49
+ .description('Generate a new widget.')
50
+ .action(async (path, name, description, tags) => {
51
+ try {
52
+ // Validate if the workspace is initialized
53
+ const validWorkspacePath = await validateWorkspace();
54
+ const rootPath = process.cwd();
55
+ // Load the workspace config
56
+ const workspaceConfig = await loadWorkspace(validWorkspacePath);
57
+ // Default to current dir if the path wasn't provided
58
+ const resolvedPath = path ? resolve(rootPath, path.replace(/[/\\]$/, '')) : rootPath;
59
+ const isTarget = path ? path.endsWith('/') || path.endsWith('\\') : false;
60
+ console.log('🎨 Generating widget...\n');
61
+ if (isTarget) {
62
+ // Path ends with / -> use as target directory
63
+ const folderName = basename(resolvedPath);
64
+ await createWidget(resolvedPath, {
65
+ name: folderName,
66
+ description,
67
+ tags: tags ? tags.split(',').map((t) => t.trim()) : undefined,
68
+ }, workspaceConfig, validWorkspacePath);
69
+ }
70
+ else {
71
+ // Path without / -> use as parent directory
72
+ let finalWidgetName = name;
73
+ if (!finalWidgetName) {
74
+ // Get next number and ask for name
75
+ const nextNum = await getNextWidgetNumber(resolvedPath);
76
+ const defaultName = `${nextNum} - Widget`;
77
+ const answers = await inquirer.prompt([
78
+ {
79
+ type: 'input',
80
+ name: 'name',
81
+ message: 'Widget name:',
82
+ default: defaultName,
83
+ },
84
+ ]);
85
+ finalWidgetName = answers.name;
30
86
  }
87
+ const widgetPath = join(resolvedPath, finalWidgetName);
88
+ await createWidget(widgetPath, {
89
+ name: finalWidgetName,
90
+ description,
91
+ tags: tags ? tags.split(',').map((t) => t.trim()) : undefined,
92
+ }, workspaceConfig, validWorkspacePath);
93
+ }
94
+ console.log('\n✨ Generation complete!');
95
+ }
96
+ catch (error) {
97
+ throw error;
98
+ }
99
+ });
100
+ program
101
+ .command('build')
102
+ .aliases(['b', 'compact', 'compile'])
103
+ .description('Build widgets in the workspace. Supports interactive selection and version bumping.')
104
+ .option('-d --depth <number>', 'Maximum search depth for widgets', '3')
105
+ .option('-p --parallel', 'Build widgets in parallel')
106
+ .option('-v --verbose', 'Show verbose output')
107
+ .action(async (options = {}) => {
108
+ try {
109
+ // Validate if the workspace is initialized
110
+ const validWorkspacePath = await validateWorkspace();
111
+ const rootPath = process.cwd();
112
+ // Load the workspace config
113
+ const workspaceConfig = await loadWorkspace(validWorkspacePath);
114
+ const maxDepth = options.depth ? parseInt(options.depth, 10) : workspaceConfig.search?.maxDepth || 3;
115
+ console.log(`🔍 Searching for widgets (max depth: ${maxDepth})...\n`);
116
+ // Find all widgets on the workspace
117
+ const widgets = await findWidgets(rootPath, maxDepth, workspaceConfig.search?.ignore || []);
118
+ const choices = widgets.map((widget) => ({
119
+ name: `${widget.config.name} (${widget.relativePath})`,
120
+ value: widget.path,
121
+ checked: false,
122
+ }));
123
+ let selectedPaths = [];
124
+ if (widgets.length === 0) {
125
+ console.log('❌ No widgets found with .tixyel configuration files.');
126
+ return;
31
127
  }
32
- catch (error) {
33
- console.error(`Failed to load command from ${file}:`, error);
128
+ else if (widgets.length === 1) {
129
+ selectedPaths = [choices[0].value];
130
+ console.log(`🔒 Only one widget found, auto-selected: ${choices[0].name}\n`);
34
131
  }
132
+ else {
133
+ console.log(`✅ Found ${widgets.length} widget(s)\n`);
134
+ const answers = await inquirer.prompt([
135
+ {
136
+ type: 'checkbox',
137
+ name: 'selectedWidgets',
138
+ message: 'Select widgets to build:',
139
+ choices,
140
+ pageSize: 10,
141
+ loop: false,
142
+ },
143
+ ]);
144
+ selectedPaths = answers.selectedWidgets;
145
+ }
146
+ if (selectedPaths.length === 0) {
147
+ console.log('❌ No widgets selected for build. Exiting.');
148
+ return;
149
+ }
150
+ const versionAnswers = await inquirer.prompt([
151
+ {
152
+ type: 'select',
153
+ name: 'versionBump',
154
+ message: 'Select version bump type:',
155
+ choices: [
156
+ { name: 'No version bump', value: 'none' },
157
+ { name: 'Patch (x.x.1)', value: 'patch' },
158
+ { name: 'Minor (x.1.x)', value: 'minor' },
159
+ { name: 'Major (1.x.x)', value: 'major' },
160
+ ],
161
+ default: 'none',
162
+ loop: false,
163
+ pageSize: 4,
164
+ },
165
+ ]);
166
+ const versionBump = versionAnswers.versionBump;
167
+ // Resolve parallel option (CLI option overrides config)
168
+ const buildInParallel = options.parallel ?? workspaceConfig.build?.parallel ?? false;
169
+ const verboseOutput = options.verbose ?? workspaceConfig.build?.verbose ?? false;
170
+ console.log(`\n⚙️ Building ${selectedPaths.length} widget(s)${buildInParallel ? ' in parallel' : ''}...\n`);
171
+ if (buildInParallel) {
172
+ await Promise.all(selectedPaths.map(async (path) => {
173
+ const widget = widgets.find((w) => w.path === path);
174
+ if (widget) {
175
+ await build(widget, versionBump, verboseOutput, workspaceConfig);
176
+ }
177
+ }));
178
+ }
179
+ else {
180
+ for (const widgetPath of selectedPaths) {
181
+ const widget = widgets.find((w) => w.path === widgetPath);
182
+ if (widget) {
183
+ await build(widget, versionBump, verboseOutput, workspaceConfig);
184
+ }
185
+ }
186
+ }
187
+ console.log('\n🎉 Build process complete!');
188
+ }
189
+ catch (error) {
190
+ throw error;
35
191
  }
36
- }
37
- // Load commands and parse
38
- await loadCommands();
192
+ });
39
193
  program.parse();
194
+ export * from './workspace.js';
@@ -0,0 +1 @@
1
+ export declare const workspace_config: string;
@@ -0,0 +1,111 @@
1
+ export const workspace_config = `
2
+ import { defineWorkspaceConfig } from '@tixyel/cli';
3
+
4
+ export default defineWorkspaceConfig({
5
+ search: {
6
+ maxDepth: 3,
7
+ ignore: ['node_modules', 'dist', 'build', '.git'],
8
+ },
9
+
10
+ metadata: {
11
+ author: 'Your Name',
12
+ clientId: 'your-client-id',
13
+ },
14
+
15
+ scaffold: [
16
+ {
17
+ name: 'development',
18
+ type: 'folder',
19
+ content: [
20
+ {
21
+ name: 'index.html',
22
+ type: 'file',
23
+ content: \`\`,
24
+ },
25
+ {
26
+ name: 'style.css',
27
+ type: 'file',
28
+ content: \`\`,
29
+ },
30
+ {
31
+ name: 'script.js',
32
+ type: 'file',
33
+ content: \`\`,
34
+ },
35
+ {
36
+ name: 'fields.json',
37
+ type: 'file',
38
+ content: '{}',
39
+ },
40
+ {
41
+ name: 'data.json',
42
+ type: 'file',
43
+ content: '{}',
44
+ },
45
+ ],
46
+ },
47
+ {
48
+ name: 'finished',
49
+ type: 'folder',
50
+ },
51
+ {
52
+ name: 'widgetIO',
53
+ type: 'folder',
54
+ },
55
+ ],
56
+
57
+ build: {
58
+ parallel: true,
59
+ verbose: false,
60
+
61
+ find: {
62
+ html: ['index.html'],
63
+ script: ['script.js'],
64
+ css: ['style.css'],
65
+ fields: ['fields.json', 'fields.jsonc'],
66
+ },
67
+ result: {
68
+ 'HTML.html': 'html',
69
+ 'SCRIPT.js': 'script',
70
+ 'CSS.css': 'css',
71
+ 'FIELDS.json': 'fields',
72
+ },
73
+ widgetIO: {
74
+ 'html.txt': 'html',
75
+ 'js.txt': 'script',
76
+ 'css.txt': 'css',
77
+ 'fields.txt': 'fields',
78
+ },
79
+
80
+ obfuscation: {
81
+ javascript: {
82
+ compact: true,
83
+ log: false,
84
+ debugProtection: false,
85
+ selfDefending: false,
86
+ deadCodeInjection: false,
87
+ controlFlowFlattening: false,
88
+ stringArray: false,
89
+ simplify: false,
90
+ identifierNamesGenerator: 'mangled',
91
+ },
92
+ css: {
93
+ removeNesting: true,
94
+ autoprefixer: {
95
+ overrideBrowserslist: ['Chrome 127'],
96
+ },
97
+ cssnano: {
98
+ preset: 'default',
99
+ },
100
+ },
101
+ html: {
102
+ removeComments: true,
103
+ collapseWhitespace: true,
104
+ minifyCSS: true,
105
+ minifyJS: true,
106
+ removeAttributeQuotes: false,
107
+ },
108
+ },
109
+ },
110
+ });
111
+ `.trim();
@@ -0,0 +1,31 @@
1
+ import { WorkspaceConfig } from './workspace';
2
+ export interface DotTixyel {
3
+ name: string;
4
+ description: string;
5
+ version: string;
6
+ config?: string;
7
+ metadata: WorkspaceConfig['metadata'];
8
+ dirs: WorkspaceConfig['dirs'];
9
+ }
10
+ export declare function createWidget(path: string, metadata: WorkspaceConfig['metadata'], config: WorkspaceConfig, root: string): Promise<void>;
11
+ export declare function getNextWidgetNumber(parentPath: string): Promise<string>;
12
+ export declare function validateDotTixyel(config: DotTixyel): boolean;
13
+ export type WidgetInfo = {
14
+ /**
15
+ * Absolute path to the widget directory
16
+ */
17
+ path: string;
18
+ /**
19
+ * Relative path from the root directory
20
+ */
21
+ relativePath: string;
22
+ /**
23
+ * Parsed .tixyel configuration
24
+ */
25
+ config: DotTixyel;
26
+ };
27
+ export declare function readDotTixyel(path: string): Promise<DotTixyel | null>;
28
+ export declare function findWidgets(root: string, depth: number, ignore: string[]): Promise<WidgetInfo[]>;
29
+ export declare function build(widget: WidgetInfo, versionBump: 'none' | 'patch' | 'minor' | 'major', verbose: boolean | undefined, workspaceConfig: WorkspaceConfig): Promise<void>;
30
+ export declare function processBuild(widget: WidgetInfo, workspaceConfig: WorkspaceConfig, verbose?: boolean): Promise<void>;
31
+ export declare function bumpVersion(widgetPath: string, bumpType?: 'major' | 'minor' | 'patch'): Promise<string | null>;