@tixyel/cli 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.
@@ -0,0 +1,140 @@
1
+ import { writeFile } from 'fs/promises';
2
+ import { resolve } from 'path';
3
+ import { Command } from './base.js';
4
+ /**
5
+ * Init command - initializes a workspace
6
+ */
7
+ export class InitCommand extends Command {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.name = 'init';
11
+ this.description = 'Initialize a new Tixyel workspace';
12
+ }
13
+ async execute() {
14
+ const rootPath = process.cwd();
15
+ const configPath = resolve(rootPath, 'tixyel.config.ts');
16
+ console.log('🚀 Initializing Tixyel workspace...\n');
17
+ console.log(`📁 Workspace root: ${rootPath}\n`);
18
+ // Create initial tixyel.config.ts
19
+ const configContent = `import type { TixyelCliConfig } from '@tixyel/cli';
20
+
21
+ /**
22
+ * Tixyel workspace configuration
23
+ */
24
+ const config: TixyelCliConfig = {
25
+ // Search configuration
26
+ search: {
27
+ maxDepth: 3,
28
+ ignore: ['node_modules', 'dist', '.git', '.turbo', '.vscode'],
29
+ },
30
+
31
+ // Defaults for widget generation
32
+ generationDefaults: {
33
+ author: 'Your Name',
34
+ minify: true,
35
+ platform: 'streamelements',
36
+
37
+ // File structure to create when generating a widget
38
+ scaffold: [
39
+ {
40
+ name: 'development',
41
+ type: 'folder',
42
+ content: [
43
+ {
44
+ name: 'index.html',
45
+ type: 'file',
46
+ content: '',
47
+ },
48
+ {
49
+ name: 'script.js',
50
+ type: 'file',
51
+ content: '',
52
+ },
53
+ {
54
+ name: 'fields.json',
55
+ type: 'file',
56
+ content: '{}',
57
+ },
58
+ {
59
+ name: 'data.json',
60
+ type: 'file',
61
+ content: '{}',
62
+ },
63
+ {
64
+ name: 'style.css',
65
+ type: 'file',
66
+ content: '',
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ name: 'finished',
72
+ type: 'folder',
73
+ },
74
+ ],
75
+ },
76
+
77
+ // Build configuration
78
+ build: {
79
+ parallel: false,
80
+ verbose: false,
81
+ find: {
82
+ html: ['index.html'],
83
+ css: ['style.css'],
84
+ script: ['resources.js', 'script.js'],
85
+ fields: ['cf.json', 'fields.json'],
86
+ },
87
+ finished: {
88
+ 'HTML.html': 'html',
89
+ 'CSS.css': 'css',
90
+ 'SCRIPT.js': 'script',
91
+ 'FIELDS.json': 'fields',
92
+ },
93
+ obfuscation: {
94
+ javascript: {
95
+ compact: true,
96
+ log: false,
97
+ debugProtection: false,
98
+ selfDefending: false,
99
+ deadCodeInjection: false,
100
+ controlFlowFlattening: false,
101
+ stringArray: false,
102
+ simplify: false,
103
+ identifierNamesGenerator: 'mangled',
104
+ },
105
+ css: {
106
+ removeNesting: true,
107
+ autoprefixer: {
108
+ overrideBrowserslist: ['Chrome 127'],
109
+ },
110
+ cssnano: {
111
+ preset: 'default',
112
+ },
113
+ },
114
+ html: {
115
+ removeComments: true,
116
+ collapseWhitespace: true,
117
+ minifyCSS: true,
118
+ minifyJS: true,
119
+ removeAttributeQuotes: false,
120
+ },
121
+ },
122
+ },
123
+ };
124
+
125
+ export default config;
126
+ `;
127
+ try {
128
+ await writeFile(configPath, configContent, 'utf-8');
129
+ console.log('✅ Created tixyel.config.ts');
130
+ console.log(' Edit it to customize your workspace settings');
131
+ console.log('\n✨ Workspace initialized! You can now use:');
132
+ console.log(' - tixyel generate [path] - Generate new widgets');
133
+ console.log(' - tixyel build - Build selected widgets');
134
+ }
135
+ catch (error) {
136
+ console.error(`❌ Failed to initialize workspace: ${error}`);
137
+ throw error;
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export type { TixyelCliConfig, ScaffoldItem, ScaffoldFile, ScaffoldFolder } from './types/tixyel-cli-config.js';
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
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
+ }
30
+ }
31
+ }
32
+ catch (error) {
33
+ console.error(`Failed to load command from ${file}:`, error);
34
+ }
35
+ }
36
+ }
37
+ // Load commands and parse
38
+ await loadCommands();
39
+ program.parse();
@@ -0,0 +1,149 @@
1
+ import type { Options as AutoprefixerOptions } from 'autoprefixer';
2
+ import type { Options as CssnanoOptions } from 'cssnano';
3
+ import type { ObfuscatorOptions } from 'javascript-obfuscator';
4
+ import type { Options as HtmlMinifierOptions } from 'html-minifier-terser';
5
+ /**
6
+ * Schema for tixyel.config.ts configuration file
7
+ */
8
+ export interface TixyelCliConfig {
9
+ /**
10
+ * Search configuration
11
+ */
12
+ search?: {
13
+ /**
14
+ * Maximum depth to search for .tixyel files
15
+ */
16
+ maxDepth?: number;
17
+ /**
18
+ * Folders to ignore during search
19
+ */
20
+ ignore?: string[];
21
+ };
22
+ /**
23
+ * Defaults for generating new widgets
24
+ */
25
+ generationDefaults?: {
26
+ /**
27
+ * Default author for new widgets
28
+ */
29
+ author?: string;
30
+ /**
31
+ * Default minify setting
32
+ */
33
+ minify?: boolean;
34
+ /**
35
+ * Default platform
36
+ */
37
+ platform?: 'streamelements';
38
+ /**
39
+ * File/folder structure to create when generating a widget
40
+ */
41
+ scaffold?: ScaffoldItem[];
42
+ };
43
+ /**
44
+ * Build configuration
45
+ */
46
+ build?: {
47
+ /**
48
+ * Run builds in parallel
49
+ */
50
+ parallel?: boolean;
51
+ /**
52
+ * Verbose output
53
+ */
54
+ verbose?: boolean;
55
+ /**
56
+ * Default file patterns for widgets
57
+ */
58
+ find?: {
59
+ html?: string[];
60
+ css?: string[];
61
+ script?: string[];
62
+ fields?: string[];
63
+ };
64
+ /**
65
+ * Default output file mapping
66
+ */
67
+ finished?: {
68
+ [key: string]: 'html' | 'css' | 'script' | 'fields';
69
+ };
70
+ /**
71
+ * Obfuscation options per file type
72
+ */
73
+ obfuscation?: {
74
+ /**
75
+ * JavaScript obfuscation options
76
+ */
77
+ javascript?: ObfuscatorOptions;
78
+ /**
79
+ * CSS processing options
80
+ */
81
+ css?: {
82
+ removeNesting?: boolean;
83
+ autoprefixer?: AutoprefixerOptions;
84
+ cssnano?: CssnanoOptions;
85
+ };
86
+ /**
87
+ * HTML minification options
88
+ */
89
+ html?: HtmlMinifierOptions;
90
+ };
91
+ };
92
+ }
93
+ /**
94
+ * Represents a file or folder in the scaffold
95
+ */
96
+ export type ScaffoldItem = ScaffoldFile | ScaffoldFolder;
97
+ /**
98
+ * File in scaffold
99
+ */
100
+ export interface ScaffoldFile {
101
+ name: string;
102
+ type: 'file';
103
+ content: string;
104
+ }
105
+ /**
106
+ * Folder in scaffold
107
+ */
108
+ export interface ScaffoldFolder {
109
+ name: string;
110
+ type: 'folder';
111
+ content?: ScaffoldItem[];
112
+ }
113
+ /**
114
+ * Required configuration with all defaults
115
+ */
116
+ export interface RequiredCliConfig {
117
+ search: Required<NonNullable<TixyelCliConfig['search']>>;
118
+ generationDefaults: Required<NonNullable<TixyelCliConfig['generationDefaults']>>;
119
+ build: {
120
+ parallel: boolean;
121
+ verbose: boolean;
122
+ find: {
123
+ html: string[];
124
+ css: string[];
125
+ script: string[];
126
+ fields: string[];
127
+ };
128
+ finished: {
129
+ [key: string]: 'html' | 'css' | 'script' | 'fields';
130
+ };
131
+ obfuscation: {
132
+ javascript: ObfuscatorOptions;
133
+ css: {
134
+ removeNesting: boolean;
135
+ autoprefixer: AutoprefixerOptions;
136
+ cssnano: CssnanoOptions;
137
+ };
138
+ html: HtmlMinifierOptions;
139
+ };
140
+ };
141
+ }
142
+ /**
143
+ * Default CLI configuration
144
+ */
145
+ export declare const DEFAULT_CLI_CONFIG: RequiredCliConfig;
146
+ /**
147
+ * Merges user config with defaults
148
+ */
149
+ export declare function mergeCliConfig(userConfig: TixyelCliConfig | undefined): RequiredCliConfig;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Default CLI configuration
3
+ */
4
+ export const DEFAULT_CLI_CONFIG = {
5
+ search: {
6
+ maxDepth: 3,
7
+ ignore: ['node_modules', 'dist', '.git', '.turbo'],
8
+ },
9
+ generationDefaults: {
10
+ author: 'Tixyel',
11
+ minify: true,
12
+ platform: 'streamelements',
13
+ scaffold: [
14
+ {
15
+ name: 'development',
16
+ type: 'folder',
17
+ content: [
18
+ {
19
+ name: 'index.html',
20
+ type: 'file',
21
+ content: `<!DOCTYPE html>
22
+ <html lang="en">
23
+ <head>
24
+ <meta charset="UTF-8">
25
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
26
+ <title>Widget</title>
27
+ <link rel="stylesheet" href="style.css">
28
+ </head>
29
+ <body>
30
+ <div id="widget">
31
+ <!-- Widget content here -->
32
+ </div>
33
+ <script src="script.js"></script>
34
+ </body>
35
+ </html>`,
36
+ },
37
+ {
38
+ name: 'script.js',
39
+ type: 'file',
40
+ content: `// Widget script
41
+ console.log('Widget loaded');
42
+ `,
43
+ },
44
+ {
45
+ name: 'fields.json',
46
+ type: 'file',
47
+ content: `{
48
+ "title": "Widget Fields",
49
+ "fields": []
50
+ }`,
51
+ },
52
+ {
53
+ name: 'data.json',
54
+ type: 'file',
55
+ content: `{
56
+ "title": "Widget Data"
57
+ }`,
58
+ },
59
+ {
60
+ name: 'style.css',
61
+ type: 'file',
62
+ content: `/* Widget styles */
63
+ body {
64
+ margin: 0;
65
+ padding: 0;
66
+ }
67
+
68
+ #widget {
69
+ /* Add your styles here */
70
+ }`,
71
+ },
72
+ ],
73
+ },
74
+ {
75
+ name: 'finished',
76
+ type: 'folder',
77
+ },
78
+ ],
79
+ },
80
+ build: {
81
+ parallel: false,
82
+ verbose: false,
83
+ find: {
84
+ html: ['index.html'],
85
+ css: ['style.css'],
86
+ script: ['resources.js', 'script.js'],
87
+ fields: ['cf.json', 'fields.json'],
88
+ },
89
+ finished: {
90
+ 'HTML.html': 'html',
91
+ 'CSS.css': 'css',
92
+ 'SCRIPT.js': 'script',
93
+ 'FIELDS.json': 'fields',
94
+ },
95
+ obfuscation: {
96
+ javascript: {
97
+ compact: true,
98
+ log: false,
99
+ debugProtection: false,
100
+ selfDefending: false,
101
+ deadCodeInjection: false,
102
+ controlFlowFlattening: false,
103
+ stringArray: false,
104
+ simplify: false,
105
+ identifierNamesGenerator: 'mangled',
106
+ },
107
+ css: {
108
+ removeNesting: true,
109
+ autoprefixer: {
110
+ overrideBrowserslist: ['Chrome 127'],
111
+ },
112
+ cssnano: {
113
+ preset: 'default',
114
+ },
115
+ },
116
+ html: {
117
+ removeComments: true,
118
+ collapseWhitespace: true,
119
+ minifyCSS: true,
120
+ minifyJS: true,
121
+ removeAttributeQuotes: false,
122
+ },
123
+ },
124
+ },
125
+ };
126
+ /**
127
+ * Merges user config with defaults
128
+ */
129
+ export function mergeCliConfig(userConfig) {
130
+ return {
131
+ search: {
132
+ maxDepth: userConfig?.search?.maxDepth ?? DEFAULT_CLI_CONFIG.search.maxDepth,
133
+ ignore: userConfig?.search?.ignore ?? DEFAULT_CLI_CONFIG.search.ignore,
134
+ },
135
+ generationDefaults: {
136
+ author: userConfig?.generationDefaults?.author ?? DEFAULT_CLI_CONFIG.generationDefaults.author,
137
+ minify: userConfig?.generationDefaults?.minify ?? DEFAULT_CLI_CONFIG.generationDefaults.minify,
138
+ platform: userConfig?.generationDefaults?.platform ?? DEFAULT_CLI_CONFIG.generationDefaults.platform,
139
+ scaffold: userConfig?.generationDefaults?.scaffold ?? DEFAULT_CLI_CONFIG.generationDefaults.scaffold,
140
+ },
141
+ build: {
142
+ parallel: userConfig?.build?.parallel ?? DEFAULT_CLI_CONFIG.build.parallel,
143
+ verbose: userConfig?.build?.verbose ?? DEFAULT_CLI_CONFIG.build.verbose,
144
+ find: {
145
+ html: userConfig?.build?.find?.html ?? DEFAULT_CLI_CONFIG.build.find.html,
146
+ css: userConfig?.build?.find?.css ?? DEFAULT_CLI_CONFIG.build.find.css,
147
+ script: userConfig?.build?.find?.script ?? DEFAULT_CLI_CONFIG.build.find.script,
148
+ fields: userConfig?.build?.find?.fields ?? DEFAULT_CLI_CONFIG.build.find.fields,
149
+ },
150
+ finished: userConfig?.build?.finished ?? DEFAULT_CLI_CONFIG.build.finished,
151
+ obfuscation: {
152
+ javascript: userConfig?.build?.obfuscation?.javascript ?? DEFAULT_CLI_CONFIG.build.obfuscation.javascript,
153
+ css: {
154
+ removeNesting: userConfig?.build?.obfuscation?.css?.removeNesting ?? DEFAULT_CLI_CONFIG.build.obfuscation.css.removeNesting,
155
+ autoprefixer: userConfig?.build?.obfuscation?.css?.autoprefixer ?? DEFAULT_CLI_CONFIG.build.obfuscation.css.autoprefixer,
156
+ cssnano: userConfig?.build?.obfuscation?.css?.cssnano ?? DEFAULT_CLI_CONFIG.build.obfuscation.css.cssnano,
157
+ },
158
+ html: userConfig?.build?.obfuscation?.html ?? DEFAULT_CLI_CONFIG.build.obfuscation.html,
159
+ },
160
+ },
161
+ };
162
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Schema for .tixyel configuration file
3
+ */
4
+ export interface TixyelConfig {
5
+ /**
6
+ * Widget name
7
+ */
8
+ name: string;
9
+ /**
10
+ * Widget version (default: 1.0.0)
11
+ */
12
+ version?: string;
13
+ /**
14
+ * Widget description
15
+ */
16
+ description?: string;
17
+ /**
18
+ * Entry point directory (default: development)
19
+ */
20
+ entry?: string;
21
+ /**
22
+ * Output directory (default: finished)
23
+ */
24
+ outDir?: string;
25
+ /**
26
+ * Relative path to tixyel.config.ts from this file's directory
27
+ * Auto-calculated during widget generation
28
+ */
29
+ configPath?: string;
30
+ /**
31
+ * Additional build configuration
32
+ */
33
+ build?: {
34
+ /**
35
+ * Minify output
36
+ */
37
+ minify?: boolean;
38
+ /**
39
+ * File patterns to find and merge
40
+ */
41
+ find?: {
42
+ html?: string[];
43
+ css?: string[];
44
+ script?: string[];
45
+ fields?: string[];
46
+ };
47
+ /**
48
+ * Output file mapping
49
+ */
50
+ finished?: {
51
+ [key: string]: 'html' | 'css' | 'script' | 'fields';
52
+ };
53
+ };
54
+ /**
55
+ * Widget metadata
56
+ */
57
+ metadata?: {
58
+ author?: string;
59
+ tags?: string[];
60
+ /**
61
+ * Platform - currently only StreamElements is supported
62
+ */
63
+ platform?: 'streamelements';
64
+ };
65
+ }
66
+ /**
67
+ * Default configuration values
68
+ */
69
+ export declare const DEFAULT_TIXYEL_CONFIG: {
70
+ readonly version: "1.0.0";
71
+ readonly entry: "development";
72
+ readonly outDir: "finished";
73
+ };
74
+ /**
75
+ * Validates a Tixyel configuration object
76
+ */
77
+ export declare function validateTixyelConfig(config: unknown): config is TixyelConfig;
78
+ /**
79
+ * Applies default values to a configuration object
80
+ */
81
+ export declare function applyDefaults(config: TixyelConfig): Omit<Required<TixyelConfig>, 'configPath'> & {
82
+ configPath?: string;
83
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Default configuration values
3
+ */
4
+ export const DEFAULT_TIXYEL_CONFIG = {
5
+ version: '1.0.0',
6
+ entry: 'development',
7
+ outDir: 'finished',
8
+ };
9
+ /**
10
+ * Validates a Tixyel configuration object
11
+ */
12
+ export function validateTixyelConfig(config) {
13
+ if (typeof config !== 'object' || config === null) {
14
+ return false;
15
+ }
16
+ const cfg = config;
17
+ // Required fields
18
+ if (typeof cfg.name !== 'string' || !cfg.name) {
19
+ return false;
20
+ }
21
+ return true;
22
+ }
23
+ /**
24
+ * Applies default values to a configuration object
25
+ */
26
+ export function applyDefaults(config) {
27
+ return {
28
+ name: config.name,
29
+ version: config.version || DEFAULT_TIXYEL_CONFIG.version,
30
+ description: config.description ?? '',
31
+ entry: config.entry ?? DEFAULT_TIXYEL_CONFIG.entry,
32
+ outDir: config.outDir ?? DEFAULT_TIXYEL_CONFIG.outDir,
33
+ configPath: config.configPath,
34
+ build: config.build
35
+ ? {
36
+ minify: config.build.minify ?? false,
37
+ find: config.build.find,
38
+ finished: config.build.finished,
39
+ }
40
+ : { minify: false },
41
+ metadata: config.metadata ?? { platform: 'streamelements' },
42
+ };
43
+ }
@@ -0,0 +1,12 @@
1
+ import type { TixyelConfig } from '../types/tixyel-config.js';
2
+ import type { RequiredCliConfig } from '../types/tixyel-cli-config.js';
3
+ export interface BuildOptions {
4
+ widgetPath: string;
5
+ config: TixyelConfig;
6
+ cliConfig: RequiredCliConfig;
7
+ verbose?: boolean;
8
+ }
9
+ /**
10
+ * Processes and builds a widget
11
+ */
12
+ export declare function buildWidget(options: BuildOptions): Promise<void>;