@tixyel/cli 2.7.1 → 3.1.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.
- package/dist/api.d.mts +144 -0
- package/dist/api.mjs +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +117 -0
- package/dist/workspace-BnA15bR0.mjs +33 -0
- package/package.json +20 -14
- package/dist/api.d.ts +0 -2
- package/dist/api.js +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -416
- package/dist/templates/workspace.d.ts +0 -1
- package/dist/templates/workspace.js +0 -111
- package/dist/utils/watermark.d.ts +0 -7
- package/dist/utils/watermark.js +0 -71
- package/dist/widget.d.ts +0 -31
- package/dist/widget.js +0 -551
- package/dist/widget.test.d.ts +0 -1
- package/dist/widget.test.js +0 -14
- package/dist/workspace.d.ts +0 -113
- package/dist/workspace.js +0 -241
package/dist/widget.js
DELETED
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
-
import { minify as minifyHTML } from 'html-minifier-terser';
|
|
3
|
-
import JavaScriptObfuscator from 'javascript-obfuscator';
|
|
4
|
-
import { dirname, join, relative, resolve } from 'path';
|
|
5
|
-
import { readFile as readFilePromise } from 'fs/promises';
|
|
6
|
-
import { renderToStaticMarkup } from 'react-dom/server';
|
|
7
|
-
import { mkdir, writeFile } from 'fs/promises';
|
|
8
|
-
import autoprefixer from 'autoprefixer';
|
|
9
|
-
import { isValidElement } from 'react';
|
|
10
|
-
import { parse } from 'jsonc-parser';
|
|
11
|
-
import nested from 'postcss-nested';
|
|
12
|
-
import FastGlob from 'fast-glob';
|
|
13
|
-
import inquirer from 'inquirer';
|
|
14
|
-
import postcss from 'postcss';
|
|
15
|
-
import cssnano from 'cssnano';
|
|
16
|
-
import JSZip from 'jszip';
|
|
17
|
-
import { transformSync } from 'esbuild';
|
|
18
|
-
import { watermark } from './utils/watermark.js';
|
|
19
|
-
export async function createWidget(path, metadata, config, root) {
|
|
20
|
-
try {
|
|
21
|
-
console.log(`📁 Creating ${metadata?.name} at: ${path}`);
|
|
22
|
-
// Create directory
|
|
23
|
-
await mkdir(path, { recursive: true });
|
|
24
|
-
// Calculate config path if workspace root is provided
|
|
25
|
-
const configPath = root ? getConfigPathFromWidget(path, root) : undefined;
|
|
26
|
-
// Prompt for metadata if not provided
|
|
27
|
-
const widgetMetadata = await getMetadata(metadata);
|
|
28
|
-
// Create .tixyel configuration
|
|
29
|
-
const dotTixyel = {
|
|
30
|
-
name: metadata?.name,
|
|
31
|
-
version: '0.0.0',
|
|
32
|
-
description: widgetMetadata?.description || '',
|
|
33
|
-
config: configPath,
|
|
34
|
-
metadata: {
|
|
35
|
-
...config.metadata,
|
|
36
|
-
...widgetMetadata,
|
|
37
|
-
name: undefined,
|
|
38
|
-
description: undefined,
|
|
39
|
-
},
|
|
40
|
-
dirs: config.dirs || {
|
|
41
|
-
entry: 'development',
|
|
42
|
-
output: 'finished',
|
|
43
|
-
compacted: 'compacted',
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
await writeFile(resolve(path, '.tixyel'), JSON.stringify(dotTixyel, null, 2), 'utf-8');
|
|
47
|
-
// Create scaffold files from the workspace config
|
|
48
|
-
const scaffold = config.scaffold || [];
|
|
49
|
-
let created = { files: 0, folders: 0 };
|
|
50
|
-
async function serializeScaffoldContent(content) {
|
|
51
|
-
if (content === undefined || content === null)
|
|
52
|
-
return '';
|
|
53
|
-
if (typeof content === 'string')
|
|
54
|
-
return content;
|
|
55
|
-
if (isValidElement(content)) {
|
|
56
|
-
return renderToStaticMarkup(content);
|
|
57
|
-
}
|
|
58
|
-
// Fallback to string conversion for unexpected types
|
|
59
|
-
return String(content ?? '');
|
|
60
|
-
}
|
|
61
|
-
async function processScaffoldItem(item, basePath) {
|
|
62
|
-
const fullPath = resolve(basePath, item.name);
|
|
63
|
-
if (item.type === 'folder') {
|
|
64
|
-
await mkdir(fullPath, { recursive: true });
|
|
65
|
-
created.folders++;
|
|
66
|
-
// Process folder children if any
|
|
67
|
-
if (item.content && Array.isArray(item.content) && item.content.length) {
|
|
68
|
-
for (const child of item.content) {
|
|
69
|
-
await processScaffoldItem(child, fullPath);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else if (item.type === 'file') {
|
|
74
|
-
const content = await serializeScaffoldContent(item.content);
|
|
75
|
-
await writeFile(fullPath, content, 'utf-8');
|
|
76
|
-
created.files++;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
for (const item of scaffold) {
|
|
80
|
-
await processScaffoldItem(item, path);
|
|
81
|
-
}
|
|
82
|
-
console.log(`✅ Widget ${dotTixyel.name} created successfully!`);
|
|
83
|
-
if (dotTixyel.description && dotTixyel.description.length) {
|
|
84
|
-
console.log(` - Description: ${dotTixyel.description}`);
|
|
85
|
-
}
|
|
86
|
-
if (dotTixyel.metadata?.tags && dotTixyel.metadata?.tags.length) {
|
|
87
|
-
console.log(` - Tags: ${dotTixyel.metadata.tags.join(', ')}`);
|
|
88
|
-
}
|
|
89
|
-
if (dotTixyel.config) {
|
|
90
|
-
console.log(` - Config Path: ${dotTixyel.config}`);
|
|
91
|
-
}
|
|
92
|
-
if (created.folders > 0 || created.files > 0) {
|
|
93
|
-
console.log(` - Scaffolded: ${created.folders} folders, ${created.files} files`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
console.error('❌ Failed to create widget:', error);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async function getMetadata(existingMetadata) {
|
|
102
|
-
const answers = await inquirer.prompt([
|
|
103
|
-
{
|
|
104
|
-
type: 'input',
|
|
105
|
-
name: 'description',
|
|
106
|
-
message: 'Widget description:',
|
|
107
|
-
default: existingMetadata?.description || '',
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
type: 'input',
|
|
111
|
-
name: 'tags',
|
|
112
|
-
message: 'Widget tags (comma-separated):',
|
|
113
|
-
default: existingMetadata?.tags ? existingMetadata.tags.join(', ') : '',
|
|
114
|
-
},
|
|
115
|
-
]);
|
|
116
|
-
const tagsArray = answers.tags
|
|
117
|
-
? answers.tags
|
|
118
|
-
.split(',')
|
|
119
|
-
.map((tag) => tag.trim())
|
|
120
|
-
.filter((tag) => tag.length)
|
|
121
|
-
: [];
|
|
122
|
-
return {
|
|
123
|
-
description: answers.description,
|
|
124
|
-
tags: tagsArray,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function getConfigPathFromWidget(widgetPath, workspaceRoot) {
|
|
128
|
-
const configTs = resolve(workspaceRoot, 'tixyel.config.ts');
|
|
129
|
-
const configJs = resolve(workspaceRoot, 'tixyel.config.js');
|
|
130
|
-
const configPathMjs = resolve(workspaceRoot, 'tixyel.config.mjs');
|
|
131
|
-
const targetConfig = existsSync(configTs) ? configTs : existsSync(configJs) ? configJs : existsSync(configPathMjs) ? configPathMjs : null;
|
|
132
|
-
if (!targetConfig) {
|
|
133
|
-
throw new Error('❌ Workspace configuration file not found.');
|
|
134
|
-
}
|
|
135
|
-
const relativePath = relative(widgetPath, targetConfig);
|
|
136
|
-
const normalized = relativePath.replace(/\\/g, '/');
|
|
137
|
-
return normalized.startsWith('.') ? normalized : `./${normalized}`;
|
|
138
|
-
}
|
|
139
|
-
export async function getNextWidgetNumber(parentPath) {
|
|
140
|
-
try {
|
|
141
|
-
if (!existsSync(parentPath)) {
|
|
142
|
-
return '01';
|
|
143
|
-
}
|
|
144
|
-
const entries = readdirSync(parentPath);
|
|
145
|
-
const widgetNumbers = entries
|
|
146
|
-
.filter((name) => /^\d+\s*-\s*/.test(name))
|
|
147
|
-
.map((name) => parseInt(name.split('-')[0], 10))
|
|
148
|
-
.filter((num) => !isNaN(num));
|
|
149
|
-
const maxNum = widgetNumbers.length > 0 ? Math.max(...widgetNumbers) : 0;
|
|
150
|
-
return String(maxNum + 1).padStart(2, '0');
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
return '01';
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async function merge(config) {
|
|
157
|
-
const merged = {
|
|
158
|
-
...config,
|
|
159
|
-
version: config.version || '0.0.0',
|
|
160
|
-
description: config.description || '',
|
|
161
|
-
metadata: {
|
|
162
|
-
...config.metadata,
|
|
163
|
-
},
|
|
164
|
-
dirs: {
|
|
165
|
-
entry: 'development',
|
|
166
|
-
output: 'finished',
|
|
167
|
-
compacted: 'compacted',
|
|
168
|
-
...config.dirs,
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
return merged;
|
|
172
|
-
}
|
|
173
|
-
export function validateDotTixyel(config) {
|
|
174
|
-
if (typeof config !== 'object' || !config || config === null || !config.name || typeof config.name !== 'string' || !config.name.trim().length) {
|
|
175
|
-
throw new Error('❌ Invalid .tixyel: "name" is required and must be a non-empty string.');
|
|
176
|
-
}
|
|
177
|
-
return true;
|
|
178
|
-
}
|
|
179
|
-
export async function readDotTixyel(path) {
|
|
180
|
-
try {
|
|
181
|
-
const dotTixyelPath = resolve(path, '.tixyel');
|
|
182
|
-
if (!existsSync(dotTixyelPath)) {
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
const content = await readFilePromise(dotTixyelPath, 'utf-8');
|
|
186
|
-
const config = parse(content);
|
|
187
|
-
if (!validateDotTixyel(config)) {
|
|
188
|
-
console.error(`Invalid .tixyel configuration in ${path}`);
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
return merge(config);
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
export async function findWidgets(root, depth, ignore) {
|
|
198
|
-
// Build glob pattern with depth limit
|
|
199
|
-
const depthPattern = Array.from({ length: depth }, (_, i) => '*'.repeat(i + 1)).join(',');
|
|
200
|
-
const pattern = `{${depthPattern}}/.tixyel`;
|
|
201
|
-
// Build ignore patterns (folders and files)
|
|
202
|
-
const ignorePatterns = ['node_modules', '.git', 'dist', ...ignore];
|
|
203
|
-
// Find all .tixyel files
|
|
204
|
-
const dotTixyels = await FastGlob(pattern, {
|
|
205
|
-
cwd: root,
|
|
206
|
-
absolute: true,
|
|
207
|
-
onlyFiles: true,
|
|
208
|
-
ignore: ignorePatterns,
|
|
209
|
-
});
|
|
210
|
-
const widgets = [];
|
|
211
|
-
for (const dotTixyelPath of dotTixyels) {
|
|
212
|
-
const path = dirname(dotTixyelPath);
|
|
213
|
-
const config = await readDotTixyel(path);
|
|
214
|
-
if (config) {
|
|
215
|
-
widgets.push({
|
|
216
|
-
path,
|
|
217
|
-
relativePath: relative(root, path),
|
|
218
|
-
config,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return widgets;
|
|
223
|
-
}
|
|
224
|
-
export async function build(widget, versionBump, verbose = false, workspaceConfig) {
|
|
225
|
-
console.log(`🔨 Building widget: ${widget.config.name}`);
|
|
226
|
-
if (verbose) {
|
|
227
|
-
console.log(` - Path: ${widget.path}`);
|
|
228
|
-
}
|
|
229
|
-
if (versionBump !== 'none') {
|
|
230
|
-
const newVersion = await bumpVersion(widget.path, versionBump);
|
|
231
|
-
if (newVersion) {
|
|
232
|
-
console.log(` - Bumped version to: ${newVersion}`);
|
|
233
|
-
widget.config.version = newVersion;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (widget.config && workspaceConfig) {
|
|
237
|
-
try {
|
|
238
|
-
await processBuild(widget, workspaceConfig, verbose);
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
console.error(`❌ Build failed for widget ${widget.config.name}:`, error);
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
console.warn(`⚠️ Skipping build for widget ${widget.config.name}: Missing configuration.`);
|
|
247
|
-
throw new Error('Missing configuration for build process.');
|
|
248
|
-
}
|
|
249
|
-
console.log(`✅ Build completed for widget: ${widget.config.name}`);
|
|
250
|
-
}
|
|
251
|
-
export async function processBuild(widget, workspaceConfig, verbose = false) {
|
|
252
|
-
const entryDir = join(widget.path, widget.config.dirs?.entry || workspaceConfig.dirs?.entry || 'development');
|
|
253
|
-
const outDir = join(widget.path, widget.config.dirs?.output || workspaceConfig.dirs?.output || 'finished');
|
|
254
|
-
const compactedDir = join(widget.path, widget.config.dirs?.compacted || workspaceConfig.dirs?.compacted || 'widgetIO');
|
|
255
|
-
if (!existsSync(entryDir)) {
|
|
256
|
-
throw new Error(`Entry directory not found: ${entryDir}`);
|
|
257
|
-
}
|
|
258
|
-
mkdirSync(outDir, { recursive: true });
|
|
259
|
-
const findPatterns = workspaceConfig.build?.find || {
|
|
260
|
-
html: ['index.html'],
|
|
261
|
-
script: ['script.js'],
|
|
262
|
-
typescript: ['script.ts'],
|
|
263
|
-
css: ['styles.css'],
|
|
264
|
-
fields: ['fields.json'],
|
|
265
|
-
};
|
|
266
|
-
const resultMapping = workspaceConfig.build?.result || {
|
|
267
|
-
'HTML.html': 'html',
|
|
268
|
-
'SCRIPT.js': 'script',
|
|
269
|
-
'CSS.css': 'css',
|
|
270
|
-
'FIELDS.json': 'fields',
|
|
271
|
-
};
|
|
272
|
-
const compactedMapping = workspaceConfig.build?.widgetIO || {
|
|
273
|
-
'html.txt': 'html',
|
|
274
|
-
'js.txt': 'script',
|
|
275
|
-
'css.txt': 'css',
|
|
276
|
-
'fields.txt': 'fields',
|
|
277
|
-
};
|
|
278
|
-
// const results: Record<string, string> = {};
|
|
279
|
-
const normalizeList = (value) => (Array.isArray(value) ? value.filter(Boolean) : []);
|
|
280
|
-
const findAndRead = (baseDir, patterns) => {
|
|
281
|
-
const contents = {};
|
|
282
|
-
for (const pattern of patterns) {
|
|
283
|
-
const fullPath = join(baseDir, pattern);
|
|
284
|
-
if (existsSync(fullPath)) {
|
|
285
|
-
const content = readFileSync(fullPath, 'utf-8');
|
|
286
|
-
contents[pattern] = content;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return contents;
|
|
290
|
-
};
|
|
291
|
-
const usedWatermarks = new Set();
|
|
292
|
-
const processedFile = new Set();
|
|
293
|
-
/**
|
|
294
|
-
* Build results processing
|
|
295
|
-
* Find all files based on patterns and process them according to their type
|
|
296
|
-
* (html, css, script, fields)
|
|
297
|
-
* Group results based on resultMapping and compactedMapping
|
|
298
|
-
* Compact/minify where applicable
|
|
299
|
-
*/
|
|
300
|
-
const results = Object.fromEntries(await Promise.all(Object.entries(findPatterns).map(async ([key, patterns]) => {
|
|
301
|
-
let result = '';
|
|
302
|
-
let list = normalizeList(patterns.filter((p) => !processedFile.has(p)));
|
|
303
|
-
if (!list.length)
|
|
304
|
-
return [key, ''];
|
|
305
|
-
const check = (keys, formats) => {
|
|
306
|
-
!Array.isArray(keys) && (keys = [keys]);
|
|
307
|
-
!Array.isArray(formats) && (formats = [formats]);
|
|
308
|
-
return (
|
|
309
|
-
// check keys
|
|
310
|
-
keys.some((k) => key.toLowerCase() === k.toLowerCase()) ||
|
|
311
|
-
// check formats
|
|
312
|
-
list.some((p) => formats.some((f) => p.toLowerCase().endsWith(f.toLowerCase()))));
|
|
313
|
-
};
|
|
314
|
-
const processed = new Set();
|
|
315
|
-
// Process HTML
|
|
316
|
-
if (check('html', '.html')) {
|
|
317
|
-
if (!usedWatermarks.has('html'))
|
|
318
|
-
result += watermark.html(widget) + '\n';
|
|
319
|
-
usedWatermarks.add('html');
|
|
320
|
-
const fileList = list.filter((e) => e.endsWith('.html') && !processedFile.has(e));
|
|
321
|
-
if (verbose)
|
|
322
|
-
console.log(` - Processing HTML for ${widget.config.name} [${key}, ${fileList.join(', ')}]...`);
|
|
323
|
-
const files = findAndRead(entryDir, fileList);
|
|
324
|
-
let mergedHTML = '';
|
|
325
|
-
for await (const [pattern, fileContent] of Object.entries(files)) {
|
|
326
|
-
// Extract body content
|
|
327
|
-
const bodyMatch = fileContent.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
328
|
-
if (bodyMatch && bodyMatch[1]) {
|
|
329
|
-
mergedHTML += bodyMatch[1].trim() + '\n';
|
|
330
|
-
processedFile.add(pattern);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
const minified = await minifyHTML(mergedHTML, workspaceConfig.build?.obfuscation?.html);
|
|
334
|
-
result += minified.trim();
|
|
335
|
-
processed.add('html');
|
|
336
|
-
}
|
|
337
|
-
if (check(['css', 'style', 'styles'], '.css')) {
|
|
338
|
-
if (!usedWatermarks.has('css'))
|
|
339
|
-
result += watermark.css(widget) + '\n';
|
|
340
|
-
usedWatermarks.add('css');
|
|
341
|
-
const fileList = list.filter((e) => e.endsWith('.css') && !processedFile.has(e));
|
|
342
|
-
if (verbose)
|
|
343
|
-
console.log(` - Processing CSS for ${widget.config.name} [${key}, ${fileList.join(', ')}]...`);
|
|
344
|
-
const files = findAndRead(entryDir, fileList);
|
|
345
|
-
let mergedCSS = '';
|
|
346
|
-
for await (const [pattern, content] of Object.entries(files)) {
|
|
347
|
-
const plugin = [
|
|
348
|
-
autoprefixer({
|
|
349
|
-
overrideBrowserslist: ['Chrome 127'],
|
|
350
|
-
...workspaceConfig.build?.obfuscation?.css?.autoprefixer,
|
|
351
|
-
}),
|
|
352
|
-
cssnano(workspaceConfig.build?.obfuscation?.css?.cssnano),
|
|
353
|
-
];
|
|
354
|
-
if (workspaceConfig.build?.obfuscation?.css?.removeNesting) {
|
|
355
|
-
plugin.unshift(nested());
|
|
356
|
-
}
|
|
357
|
-
const processed = await postcss(plugin).process(content, { from: undefined });
|
|
358
|
-
mergedCSS += processed.css + '\n';
|
|
359
|
-
processedFile.add(pattern);
|
|
360
|
-
}
|
|
361
|
-
if (processed.has('html')) {
|
|
362
|
-
result = result += `<style>${mergedCSS.trim()}</style>`;
|
|
363
|
-
}
|
|
364
|
-
else
|
|
365
|
-
result += mergedCSS.trim();
|
|
366
|
-
processed.add('css');
|
|
367
|
-
}
|
|
368
|
-
if (check(['typescript', 'ts'], '.ts')) {
|
|
369
|
-
if (!usedWatermarks.has('script'))
|
|
370
|
-
result += watermark.script(widget) + '\n';
|
|
371
|
-
usedWatermarks.add('script');
|
|
372
|
-
const fileList = list.filter((e) => e.endsWith('.ts') && !processedFile.has(e));
|
|
373
|
-
if (verbose)
|
|
374
|
-
console.log(` - Processing TypeScript for ${widget.config.name} [${key}, ${fileList.join(', ')}]...`);
|
|
375
|
-
const files = findAndRead(entryDir, fileList);
|
|
376
|
-
let mergedTS = '';
|
|
377
|
-
for await (const [pattern, content] of Object.entries(files)) {
|
|
378
|
-
try {
|
|
379
|
-
const transpiled = transformSync(content, {
|
|
380
|
-
loader: 'ts',
|
|
381
|
-
target: 'es2020',
|
|
382
|
-
format: 'cjs',
|
|
383
|
-
});
|
|
384
|
-
mergedTS += transpiled.code + '\n';
|
|
385
|
-
}
|
|
386
|
-
catch (error) {
|
|
387
|
-
console.warn(` ⚠️ Failed to compile TypeScript: ${error}`);
|
|
388
|
-
throw error;
|
|
389
|
-
}
|
|
390
|
-
finally {
|
|
391
|
-
processedFile.add(pattern);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// Obfuscate the compiled JavaScript
|
|
395
|
-
const obfuscated = JavaScriptObfuscator.obfuscate(mergedTS.trim(), workspaceConfig.build?.obfuscation?.javascript);
|
|
396
|
-
if (processed.has('html')) {
|
|
397
|
-
result = result += `<script>${obfuscated.getObfuscatedCode()}</script>\n`;
|
|
398
|
-
}
|
|
399
|
-
else
|
|
400
|
-
result += obfuscated.getObfuscatedCode() + '\n';
|
|
401
|
-
processed.add('typescript');
|
|
402
|
-
}
|
|
403
|
-
if (check(['script', 'js', 'javascript'], '.js')) {
|
|
404
|
-
if (!usedWatermarks.has('script'))
|
|
405
|
-
result += watermark.script(widget) + '\n';
|
|
406
|
-
usedWatermarks.add('script');
|
|
407
|
-
const fileList = list.filter((e) => e.endsWith('.js') && !processedFile.has(e));
|
|
408
|
-
if (verbose)
|
|
409
|
-
console.log(` - Processing JavaScript for ${widget.config.name} [${key}, ${fileList.join(', ')}]...`);
|
|
410
|
-
const files = findAndRead(entryDir, fileList);
|
|
411
|
-
let mergedJS = '';
|
|
412
|
-
for await (const [pattern, content] of Object.entries(files)) {
|
|
413
|
-
const obfuscated = JavaScriptObfuscator.obfuscate(content, workspaceConfig.build?.obfuscation?.javascript);
|
|
414
|
-
mergedJS += obfuscated.getObfuscatedCode() + '\n';
|
|
415
|
-
processedFile.add(pattern);
|
|
416
|
-
}
|
|
417
|
-
if (processed.has('html')) {
|
|
418
|
-
result = result += `<script>${mergedJS.trim()}</script>`;
|
|
419
|
-
}
|
|
420
|
-
else
|
|
421
|
-
result += mergedJS.trim();
|
|
422
|
-
processed.add('script');
|
|
423
|
-
}
|
|
424
|
-
if (check(['fields', 'fielddata', 'fieldData', 'cf', 'customfields'], '.json')) {
|
|
425
|
-
const fileList = list.filter((e) => e.endsWith('.json') && !processedFile.has(e));
|
|
426
|
-
if (verbose)
|
|
427
|
-
console.log(` - Processing JSON for ${widget.config.name} [${key}, ${fileList.join(', ')}]...`);
|
|
428
|
-
const files = findAndRead(entryDir, fileList);
|
|
429
|
-
let mergedFields = {};
|
|
430
|
-
for await (const [pattern, content] of Object.entries(files)) {
|
|
431
|
-
try {
|
|
432
|
-
const noComments = parse(content);
|
|
433
|
-
const parsed = JSON.parse(JSON.stringify(noComments));
|
|
434
|
-
Object.assign(mergedFields, parsed);
|
|
435
|
-
}
|
|
436
|
-
catch (error) {
|
|
437
|
-
console.warn(` ⚠️ Failed to parse fields JSON: ${error}`);
|
|
438
|
-
throw error;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (!processed.size)
|
|
442
|
-
result += JSON.stringify(mergedFields, null, 2);
|
|
443
|
-
processed.add('fields');
|
|
444
|
-
}
|
|
445
|
-
if (!result.length) {
|
|
446
|
-
if (verbose)
|
|
447
|
-
console.log(` - Unknown build key: ${key}, the available keys are html, css, script and fields.`);
|
|
448
|
-
result += '';
|
|
449
|
-
}
|
|
450
|
-
return [key, result];
|
|
451
|
-
})));
|
|
452
|
-
if (verbose) {
|
|
453
|
-
console.log(` - Build results:`, Object.keys(results)
|
|
454
|
-
.map((k) => `${k}: ${results[k].length} bytes`)
|
|
455
|
-
.join(', '));
|
|
456
|
-
}
|
|
457
|
-
for await (const [filename, key] of Object.entries(resultMapping)) {
|
|
458
|
-
let content = '';
|
|
459
|
-
if (typeof key === 'string')
|
|
460
|
-
content = results[key];
|
|
461
|
-
else if (Array.isArray(key)) {
|
|
462
|
-
for await (const k of key) {
|
|
463
|
-
const part = results[k];
|
|
464
|
-
if (part) {
|
|
465
|
-
content += '\n' + part;
|
|
466
|
-
if (verbose)
|
|
467
|
-
console.log(` ✓ Merged part for: ${k}`);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
if (content) {
|
|
472
|
-
const outPath = join(outDir, filename);
|
|
473
|
-
writeFileSync(outPath, content, 'utf-8');
|
|
474
|
-
if (verbose)
|
|
475
|
-
console.log(` ✓ Written: ${outPath}`);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
// widgetIO zip
|
|
479
|
-
try {
|
|
480
|
-
const zip = new JSZip();
|
|
481
|
-
for await (const [filename, key] of Object.entries(compactedMapping)) {
|
|
482
|
-
let content = '';
|
|
483
|
-
if (typeof key === 'string')
|
|
484
|
-
content = results[key];
|
|
485
|
-
else if (Array.isArray(key)) {
|
|
486
|
-
for await (const k of key) {
|
|
487
|
-
const part = results[k];
|
|
488
|
-
if (part) {
|
|
489
|
-
content += '\n' + part;
|
|
490
|
-
if (verbose)
|
|
491
|
-
console.log(` ✓ Merged part for ZIP: ${k}`);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
if (content) {
|
|
496
|
-
zip.file(filename, content);
|
|
497
|
-
if (verbose)
|
|
498
|
-
console.log(` ✓ Added to ZIP: ${filename}`);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
zip.file('widget.ini', `[HTML]\npath = "html.txt"\n\n[CSS]\npath = "css.txt"\n\n[JS]\npath = "js.txt"\n\n[FIELDS]\npath = "fields.txt"\n\n[DATA]\npath = "data.txt"`);
|
|
502
|
-
// check if data.txt exists in results, otherwise create empty
|
|
503
|
-
const dataContent = results['data'] || '{}';
|
|
504
|
-
zip.file('data.txt', dataContent);
|
|
505
|
-
const result = await zip
|
|
506
|
-
.generateInternalStream({ type: 'base64' })
|
|
507
|
-
.accumulate()
|
|
508
|
-
.then((data) => data);
|
|
509
|
-
const zipPath = join(compactedDir + '/' + widget.config.version || '0.0.0', `${widget.config.name}.zip`);
|
|
510
|
-
mkdirSync(compactedDir + '/' + widget.config.version, { recursive: true });
|
|
511
|
-
writeFileSync(zipPath, result, 'base64');
|
|
512
|
-
}
|
|
513
|
-
catch (error) {
|
|
514
|
-
console.error(`❌ Failed to create widgetIO ZIP for widget ${widget.config.name}:`, error);
|
|
515
|
-
throw error;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
export async function bumpVersion(widgetPath, bumpType = 'patch') {
|
|
519
|
-
try {
|
|
520
|
-
const configPath = join(widgetPath, '.tixyel');
|
|
521
|
-
const content = await readFilePromise(configPath, 'utf-8');
|
|
522
|
-
const config = parse(content);
|
|
523
|
-
if (!config.version) {
|
|
524
|
-
config.version = '0.0.0';
|
|
525
|
-
}
|
|
526
|
-
const [major, minor, patch] = config.version.split('.').map(Number);
|
|
527
|
-
let newVersion;
|
|
528
|
-
switch (bumpType) {
|
|
529
|
-
case 'major': {
|
|
530
|
-
newVersion = `${major + 1}.0.0`;
|
|
531
|
-
break;
|
|
532
|
-
}
|
|
533
|
-
case 'minor': {
|
|
534
|
-
newVersion = `${major}.${minor + 1}.0`;
|
|
535
|
-
break;
|
|
536
|
-
}
|
|
537
|
-
case 'patch': {
|
|
538
|
-
newVersion = `${major}.${minor}.${patch + 1}`;
|
|
539
|
-
break;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
config.version = newVersion;
|
|
543
|
-
const formattedContent = JSON.stringify(config, null, 2);
|
|
544
|
-
await writeFile(configPath, formattedContent, 'utf-8');
|
|
545
|
-
return newVersion;
|
|
546
|
-
}
|
|
547
|
-
catch (error) {
|
|
548
|
-
console.error(`Failed to bump version in ${widgetPath}:`, error);
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
}
|
package/dist/widget.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/widget.test.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { transformSync } from 'esbuild';
|
|
3
|
-
describe('Should parse typescript into javascript', () => {
|
|
4
|
-
const tsContent = `function wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms));}`;
|
|
5
|
-
const transpiled = transformSync(tsContent, {
|
|
6
|
-
loader: 'ts',
|
|
7
|
-
target: 'es2020',
|
|
8
|
-
format: 'cjs',
|
|
9
|
-
});
|
|
10
|
-
it('should transpile TypeScript to JavaScript', () => {
|
|
11
|
-
expect(transpiled.code).toContain('function wait(ms) {');
|
|
12
|
-
expect(transpiled.code).toContain('return new Promise((resolve) => setTimeout(resolve, ms));');
|
|
13
|
-
});
|
|
14
|
-
});
|
package/dist/workspace.d.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import type { Options as HtmlMinifierOptions } from 'html-minifier-terser';
|
|
2
|
-
import type { ObfuscatorOptions } from 'javascript-obfuscator';
|
|
3
|
-
import type autoprefixer from 'autoprefixer';
|
|
4
|
-
import type cssnanoPlugin from 'cssnano';
|
|
5
|
-
import type { JSX } from 'react';
|
|
6
|
-
export type ScaffoldItem = ScaffoldFile | ScaffoldFolder;
|
|
7
|
-
export interface ScaffoldFile {
|
|
8
|
-
name: string;
|
|
9
|
-
type: 'file';
|
|
10
|
-
content: string | JSX.Element;
|
|
11
|
-
}
|
|
12
|
-
export interface ScaffoldFolder {
|
|
13
|
-
name: string;
|
|
14
|
-
type: 'folder';
|
|
15
|
-
content?: ScaffoldItem[];
|
|
16
|
-
}
|
|
17
|
-
export interface WorkspaceConfig<Find extends BuildFindMap = BuildFindMap> {
|
|
18
|
-
/**
|
|
19
|
-
* Search options for locating widget files
|
|
20
|
-
*/
|
|
21
|
-
search?: {
|
|
22
|
-
/**
|
|
23
|
-
* Maximum directory depth to search
|
|
24
|
-
*/
|
|
25
|
-
maxDepth?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Folders and files to ignore during search
|
|
28
|
-
*/
|
|
29
|
-
ignore?: string[];
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Metadata applied to all widgets in the workspace
|
|
33
|
-
*/
|
|
34
|
-
metadata?: {
|
|
35
|
-
name?: string;
|
|
36
|
-
author?: string;
|
|
37
|
-
clientId?: string;
|
|
38
|
-
description?: string;
|
|
39
|
-
tags?: string[];
|
|
40
|
-
};
|
|
41
|
-
dirs?: {
|
|
42
|
-
entry?: string;
|
|
43
|
-
output?: string;
|
|
44
|
-
compacted?: string;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Scaffold structure to create when generating a new widget
|
|
48
|
-
*/
|
|
49
|
-
scaffold?: ScaffoldItem[];
|
|
50
|
-
/**
|
|
51
|
-
* Build configuration
|
|
52
|
-
*/
|
|
53
|
-
build?: {
|
|
54
|
-
/**
|
|
55
|
-
* Run builds in parallel
|
|
56
|
-
*/
|
|
57
|
-
parallel?: boolean;
|
|
58
|
-
/**
|
|
59
|
-
* Verbose output during build
|
|
60
|
-
*/
|
|
61
|
-
verbose?: boolean;
|
|
62
|
-
/**
|
|
63
|
-
* File patterns to locate widget files
|
|
64
|
-
*/
|
|
65
|
-
find?: Find;
|
|
66
|
-
/**
|
|
67
|
-
* Mapping of output files to find keys
|
|
68
|
-
*/
|
|
69
|
-
result?: BuildResultMap<Find>;
|
|
70
|
-
/**
|
|
71
|
-
* Mapping of widgetIO output files to find keys
|
|
72
|
-
*/
|
|
73
|
-
widgetIO?: BuildResultMap<Find>;
|
|
74
|
-
/**
|
|
75
|
-
* Obfuscation and minification settings
|
|
76
|
-
*/
|
|
77
|
-
obfuscation?: {
|
|
78
|
-
/**
|
|
79
|
-
* JavaScript obfuscation options
|
|
80
|
-
*/
|
|
81
|
-
javascript?: ObfuscatorOptions;
|
|
82
|
-
/**
|
|
83
|
-
* CSS minification options
|
|
84
|
-
*/
|
|
85
|
-
css?: {
|
|
86
|
-
/**
|
|
87
|
-
* Remove nesting rules
|
|
88
|
-
*/
|
|
89
|
-
removeNesting?: boolean;
|
|
90
|
-
/**
|
|
91
|
-
* Autoprefixer options
|
|
92
|
-
*/
|
|
93
|
-
autoprefixer?: autoprefixer.Options;
|
|
94
|
-
/**
|
|
95
|
-
* cssnano options
|
|
96
|
-
*/
|
|
97
|
-
cssnano?: cssnanoPlugin.Options;
|
|
98
|
-
};
|
|
99
|
-
/**
|
|
100
|
-
* HTML minification options
|
|
101
|
-
*/
|
|
102
|
-
html?: HtmlMinifierOptions;
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
type BuildFindMap = Record<string, string[]>;
|
|
107
|
-
type BuildResultMap<Find extends BuildFindMap> = Record<string, keyof Find | (keyof Find)[]>;
|
|
108
|
-
export declare function defineWorkspaceConfig<const Find extends BuildFindMap>(config: WorkspaceConfig<Find>): WorkspaceConfig<Find>;
|
|
109
|
-
export declare function resolveConfig(path: string): Promise<WorkspaceConfig>;
|
|
110
|
-
export declare function findWorkspaceRoot(startPath?: string): Promise<string | null>;
|
|
111
|
-
export declare function validateWorkspace(): Promise<string>;
|
|
112
|
-
export declare function loadWorkspace(path: string): Promise<WorkspaceConfig<BuildFindMap>>;
|
|
113
|
-
export {};
|