@tixyel/cli 1.0.0 → 2.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 (41) hide show
  1. package/dist/dev.d.ts +2 -0
  2. package/dist/dev.js +1 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +188 -33
  5. package/dist/templates/config.d.ts +1 -0
  6. package/dist/templates/config.js +12 -0
  7. package/dist/templates/workspace.d.ts +1 -0
  8. package/dist/templates/workspace.js +111 -0
  9. package/dist/types/workspace.d.ts +47 -0
  10. package/dist/types/workspace.js +4 -0
  11. package/dist/widget.d.ts +31 -0
  12. package/dist/widget.js +499 -0
  13. package/dist/workspace.d.ts +111 -0
  14. package/dist/workspace.js +225 -0
  15. package/package.json +19 -16
  16. package/dist/commands/base.d.ts +0 -24
  17. package/dist/commands/base.js +0 -15
  18. package/dist/commands/build.d.ts +0 -15
  19. package/dist/commands/build.js +0 -155
  20. package/dist/commands/generate-new.d.ts +0 -11
  21. package/dist/commands/generate-new.js +0 -146
  22. package/dist/commands/generate.d.ts +0 -11
  23. package/dist/commands/generate.js +0 -198
  24. package/dist/commands/init.d.ts +0 -9
  25. package/dist/commands/init.js +0 -140
  26. package/dist/types/tixyel-cli-config.d.ts +0 -149
  27. package/dist/types/tixyel-cli-config.js +0 -162
  28. package/dist/types/tixyel-config.d.ts +0 -83
  29. package/dist/types/tixyel-config.js +0 -43
  30. package/dist/utils/build-processor.d.ts +0 -12
  31. package/dist/utils/build-processor.js +0 -156
  32. package/dist/utils/config.d.ts +0 -23
  33. package/dist/utils/config.js +0 -34
  34. package/dist/utils/find-widgets.d.ts +0 -19
  35. package/dist/utils/find-widgets.js +0 -35
  36. package/dist/utils/load-cli-config.d.ts +0 -5
  37. package/dist/utils/load-cli-config.js +0 -66
  38. package/dist/utils/version.d.ts +0 -12
  39. package/dist/utils/version.js +0 -49
  40. package/dist/utils/workspace.d.ts +0 -13
  41. package/dist/utils/workspace.js +0 -43
package/dist/dev.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { WidgetInfo } from './widget';
2
+ export declare function devServer(widget: WidgetInfo, port?: number): Promise<void>;
package/dist/dev.js ADDED
@@ -0,0 +1 @@
1
+ export async function devServer(widget, port = 3666) { }
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 = "\nimport type { TixyelCliConfig } from '@tixyel/cli';\n\n/**\n * Tixyel workspace configuration\n */\nconst config: TixyelCliConfig = {\n \n};\n\nexport default config;\n";
@@ -0,0 +1,12 @@
1
+ export const workspace_config = `
2
+ import type { TixyelCliConfig } from '@tixyel/cli';
3
+
4
+ /**
5
+ * Tixyel workspace configuration
6
+ */
7
+ const config: TixyelCliConfig = {
8
+
9
+ };
10
+
11
+ export default config;
12
+ `;
@@ -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,47 @@
1
+ import type autoprefixer from 'autoprefixer';
2
+ import type cssnanoPlugin from 'cssnano';
3
+ import type { ObfuscatorOptions } from 'javascript-obfuscator';
4
+ import type { Options as HtmlMinifierOptions } from 'html-minifier-terser';
5
+ export type ScaffoldItem = ScaffoldFile | ScaffoldFolder;
6
+ export interface ScaffoldFile {
7
+ name: string;
8
+ type: 'file';
9
+ content: string;
10
+ }
11
+ export interface ScaffoldFolder {
12
+ name: string;
13
+ type: 'folder';
14
+ content?: ScaffoldItem[];
15
+ }
16
+ export interface WorkspaceConfig<Find extends BuildFindMap = BuildFindMap> {
17
+ search?: {
18
+ maxDepth?: number;
19
+ ignore?: string[];
20
+ };
21
+ metadata?: {
22
+ author?: string;
23
+ clientId?: string;
24
+ [key: string]: unknown;
25
+ };
26
+ scaffold?: ScaffoldItem[];
27
+ build?: {
28
+ parallel?: boolean;
29
+ verbose?: boolean;
30
+ find?: Find;
31
+ result?: BuildResultMap<Find>;
32
+ widgetIO?: BuildResultMap<Find>;
33
+ obfuscation?: {
34
+ javascript?: ObfuscatorOptions;
35
+ css?: {
36
+ removeNesting?: boolean;
37
+ autoprefixer?: autoprefixer.Options;
38
+ cssnano?: cssnanoPlugin.Options;
39
+ };
40
+ html?: HtmlMinifierOptions;
41
+ };
42
+ };
43
+ }
44
+ type BuildFindMap = Record<string, string[]>;
45
+ type BuildResultMap<Find extends BuildFindMap> = Record<string, keyof Find>;
46
+ export declare function defineWorkspaceConfig<const Find extends BuildFindMap>(config: WorkspaceConfig<Find>): WorkspaceConfig<Find>;
47
+ export {};
@@ -0,0 +1,4 @@
1
+ // Helper to preserve literal keys for autocomplete in find/result
2
+ export function defineWorkspaceConfig(config) {
3
+ return config;
4
+ }
@@ -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>;