@steambrew/ttc 1.4.2 → 2.4.2

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/Compiler.ts CHANGED
@@ -14,11 +14,12 @@ import fs from 'fs';
14
14
  import injectProcessEnv from 'rollup-plugin-inject-process-env';
15
15
  import dotenv from 'dotenv';
16
16
  import { ExecutePluginModule, InitializePlugins } from './PluginSetup';
17
+ import constSysfsExpr from './StaticEmbed';
17
18
 
18
19
  const envConfig = dotenv.config().parsed || {};
19
20
 
20
21
  if (envConfig) {
21
- Logger.Info('Injecting environment variables...');
22
+ Logger.Info('envVars', 'Processing ' + Object.keys(envConfig).length + ' environment variables... ' + chalk.green.bold('okay'));
22
23
  }
23
24
 
24
25
  const envVars = Object.keys(envConfig).reduce((acc: any, key) => {
@@ -64,7 +65,7 @@ function InsertMillennium(type: ComponentType, props: TranspilerProps) {
64
65
  continue;
65
66
  }
66
67
 
67
- Logger.Info('Injecting Millennium shims into ' + ComponentType[type] + ' module... ' + chalk.green.bold('okay'));
68
+ Logger.Info('millenniumAPI', 'Bundling into ' + ComponentType[type] + ' module... ' + chalk.green.bold('okay'));
68
69
 
69
70
  bundle[fileName].code = ConstructFunctions([
70
71
  `const MILLENNIUM_IS_CLIENT_MODULE = ${type === ComponentType.Plugin ? 'true' : 'false'};`,
@@ -98,6 +99,7 @@ function GetPluginComponents(props: TranspilerProps) {
98
99
  resolve(),
99
100
  commonjs(),
100
101
  json(),
102
+ constSysfsExpr(),
101
103
  injectProcessEnv(envVars),
102
104
  replace({
103
105
  delimiters: ['', ''],
@@ -126,6 +128,7 @@ function GetWebkitPluginComponents(props: TranspilerProps) {
126
128
  resolve(),
127
129
  commonjs(),
128
130
  json(),
131
+ constSysfsExpr(),
129
132
  injectProcessEnv(envVars),
130
133
  replace({
131
134
  delimiters: ['', ''],
@@ -182,14 +185,10 @@ export const TranspilerPluginComponent = async (props: TranspilerProps) => {
182
185
  },
183
186
  };
184
187
 
185
- Logger.Info('Starting build; this may take a few moments...');
186
-
187
188
  try {
188
189
  await (await rollup(frontendRollupConfig)).write(frontendRollupConfig.output as OutputOptions);
189
190
 
190
191
  if (fs.existsSync(`./webkit/index.tsx`)) {
191
- Logger.Info('Compiling webkit module...');
192
-
193
192
  const webkitRollupConfig: RollupOptions = {
194
193
  input: `./webkit/index.tsx`,
195
194
  plugins: GetWebkitPluginComponents(props),
@@ -218,9 +217,9 @@ export const TranspilerPluginComponent = async (props: TranspilerProps) => {
218
217
  await (await rollup(webkitRollupConfig)).write(webkitRollupConfig.output as OutputOptions);
219
218
  }
220
219
 
221
- Logger.Info('Build succeeded!', Number((performance.now() - global.PerfStartTime).toFixed(3)), 'ms elapsed.');
220
+ Logger.Info('build', 'Succeeded passing all tests in', Number((performance.now() - global.PerfStartTime).toFixed(3)), 'ms elapsed.');
222
221
  } catch (exception) {
223
- Logger.Error('Build failed!', exception);
222
+ Logger.Error('error', 'Build failed!', exception);
224
223
  process.exit(1);
225
224
  }
226
225
  };
package/Logger.ts CHANGED
@@ -1,49 +1,46 @@
1
- import chalk from 'chalk'
1
+ import chalk from 'chalk';
2
2
 
3
3
  const Logger = {
4
-
5
- Info: (...LogMessage: any) => {
6
- console.log(chalk.magenta.bold("++"), ...LogMessage)
7
- },
8
-
9
- Warn: (...LogMessage: any) => {
10
- console.log(chalk.yellow.bold("**"), ...LogMessage)
11
- },
12
-
13
- Error: (...LogMessage: any) => {
14
- console.error(chalk.red.bold("!!"), ...LogMessage)
15
- },
16
-
17
- Tree: (strTitle: string, LogObject: any) => {
18
-
19
- console.log(chalk.magenta.bold("++"), strTitle);
20
-
21
- const isLocalPath = (strTestPath: string): boolean => {
22
- // Regular expression to match common file path patterns
23
- const filePathRegex = /^(\/|\.\/|\.\.\/|\w:\/)?([\w-.]+\/)*[\w-.]+\.\w+$/;
24
- return filePathRegex.test(strTestPath);
25
- }
26
-
27
- const entries = Object.entries(LogObject);
28
- const totalEntries = entries.length;
29
-
30
- for (const [index, [key, value]] of entries.entries()) {
31
-
32
- const connector = index === totalEntries - 1 ? " " : " "
33
- let color = chalk.white
34
-
35
- switch (typeof value) {
36
- case typeof String(): {
37
- color = isLocalPath(value as string) ? chalk.blueBright : chalk.white;
38
- break
39
- }
40
- case typeof Boolean(): color = chalk.green; break
41
- case typeof Number(): color = chalk.yellow; break
42
- }
43
-
44
- console.log(chalk.magenta.bold(` ${connector}──${key}:`), color(value))
45
- }
46
- }
47
- }
48
-
49
- export { Logger }
4
+ Info: (name: string, ...LogMessage: any) => {
5
+ console.log(chalk.magenta.bold(name), ...LogMessage);
6
+ },
7
+
8
+ Warn: (...LogMessage: any) => {
9
+ console.log(chalk.yellow.bold('**'), ...LogMessage);
10
+ },
11
+
12
+ Error: (...LogMessage: any) => {
13
+ console.error(chalk.red.bold('!!'), ...LogMessage);
14
+ },
15
+
16
+ Tree: (name: string, strTitle: string, LogObject: any) => {
17
+ const fixedPadding = 15; // <-- always pad keys to 15 characters
18
+
19
+ console.log(chalk.greenBright.bold(name).padEnd(fixedPadding), strTitle);
20
+
21
+ const isLocalPath = (strTestPath: string): boolean => {
22
+ const filePathRegex = /^(\/|\.\/|\.\.\/|\w:\/)?([\w-.]+\/)*[\w-.]+\.\w+$/;
23
+ return filePathRegex.test(strTestPath);
24
+ };
25
+
26
+ for (const [key, value] of Object.entries(LogObject)) {
27
+ let color = chalk.white;
28
+
29
+ switch (typeof value) {
30
+ case 'string':
31
+ color = isLocalPath(value) ? chalk.blueBright : chalk.white;
32
+ break;
33
+ case 'boolean':
34
+ color = chalk.green;
35
+ break;
36
+ case 'number':
37
+ color = chalk.yellow;
38
+ break;
39
+ }
40
+
41
+ console.log(chalk.greenBright.bold(`${key}: `).padEnd(fixedPadding), color(String(value)));
42
+ }
43
+ },
44
+ };
45
+
46
+ export { Logger };
package/PluginSetup.ts CHANGED
@@ -141,11 +141,6 @@ function InitializePlugins() {
141
141
  MillenniumStore.ignoreProxyFlag = false;
142
142
 
143
143
  function DelegateToBackend(pluginName: string, name: string, value: any) {
144
- console.log(`Delegating ${name} to backend`, value);
145
- // print stack trace
146
- const stack = new Error().stack?.split('\n').slice(2).join('\n');
147
- console.log(stack);
148
-
149
144
  return MILLENNIUM_BACKEND_IPC.postMessage(IPCType.CallServerMethod, {
150
145
  pluginName,
151
146
  methodName: '__builtins__.__update_settings_value__',
package/StaticEmbed.ts ADDED
@@ -0,0 +1,278 @@
1
+ import { Plugin, SourceDescription, TransformPluginContext } from 'rollup';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { createFilter } from '@rollup/pluginutils';
5
+ import MagicString from 'magic-string';
6
+ import * as parser from '@babel/parser';
7
+ import traverse from '@babel/traverse';
8
+ import * as glob from 'glob';
9
+ import chalk from 'chalk';
10
+
11
+ interface EmbedPluginOptions {
12
+ include?: string | RegExp | (string | RegExp)[];
13
+ exclude?: string | RegExp | (string | RegExp)[];
14
+ encoding?: BufferEncoding;
15
+ }
16
+
17
+ interface CallOptions {
18
+ basePath?: string;
19
+ include?: string;
20
+ encoding?: BufferEncoding;
21
+ }
22
+
23
+ interface FileInfo {
24
+ content: string;
25
+ filePath: string;
26
+ fileName: string;
27
+ }
28
+
29
+ const Log = (...message: any) => {
30
+ console.log(chalk.blueBright.bold('constSysfsExpr'), ...message);
31
+ };
32
+
33
+ export default function constSysfsExpr(options: EmbedPluginOptions = {}): Plugin {
34
+ const filter = createFilter(options.include, options.exclude);
35
+ const pluginName = 'millennium-const-sysfs-expr';
36
+
37
+ return {
38
+ name: pluginName,
39
+
40
+ transform(this: TransformPluginContext, code: string, id: string): SourceDescription | null {
41
+ if (!filter(id)) return null;
42
+ if (!code.includes('constSysfsExpr')) return null;
43
+
44
+ const magicString = new MagicString(code);
45
+ let hasReplaced = false;
46
+
47
+ try {
48
+ const stringVariables = new Map<string, string>();
49
+
50
+ const ast = parser.parse(code, {
51
+ sourceType: 'module',
52
+ plugins: ['typescript', 'jsx', 'objectRestSpread', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
53
+ });
54
+
55
+ traverse(ast, {
56
+ VariableDeclarator(path) {
57
+ const init = path.node.init;
58
+ const id = path.node.id;
59
+ if (id.type === 'Identifier' && init && init.type === 'StringLiteral') {
60
+ stringVariables.set(id.name, init.value);
61
+ }
62
+ },
63
+ });
64
+
65
+ traverse(ast, {
66
+ CallExpression: (nodePath) => {
67
+ const node = nodePath.node;
68
+ if (node.callee.type === 'Identifier' && node.callee.name === 'constSysfsExpr') {
69
+ if (typeof node.start !== 'number' || typeof node.end !== 'number') {
70
+ if (node.loc) {
71
+ this.warn(`Missing start/end offset info for constSysfsExpr call.`, node.loc.start.index);
72
+ }
73
+ return;
74
+ }
75
+
76
+ const args = node.arguments;
77
+ let pathOrPattern: string | null = null;
78
+ const callOptions: Required<Omit<CallOptions, 'fileName'>> = {
79
+ basePath: '',
80
+ include: '**/*',
81
+ encoding: options.encoding || 'utf8',
82
+ };
83
+
84
+ if (args.length >= 1 && (args[0].type === 'StringLiteral' || args[0].type === 'Identifier')) {
85
+ const firstArg = args[0];
86
+ if (firstArg.type === 'StringLiteral') {
87
+ pathOrPattern = firstArg.value;
88
+ } else if (firstArg.type === 'Identifier') {
89
+ const varName = firstArg.name;
90
+ if (stringVariables.has(varName)) {
91
+ pathOrPattern = stringVariables.get(varName) || null;
92
+ } else {
93
+ this.warn(
94
+ `Unable to resolve variable "${varName}" for constSysfsExpr path/pattern. Only simple string literal assignments are supported.`,
95
+ firstArg.loc?.start.index,
96
+ );
97
+ return;
98
+ }
99
+ }
100
+
101
+ if (args.length > 1 && args[1].type === 'ObjectExpression') {
102
+ const optionsObj = args[1];
103
+ for (const prop of optionsObj.properties) {
104
+ if (prop.type !== 'ObjectProperty') continue;
105
+ let keyName: string | undefined;
106
+ if (prop.key.type === 'Identifier') keyName = prop.key.name;
107
+ else if (prop.key.type === 'StringLiteral') keyName = prop.key.value;
108
+ else continue;
109
+
110
+ if (!['basePath', 'include', 'encoding'].includes(keyName)) continue;
111
+
112
+ const valueNode = prop.value;
113
+ if (valueNode.type === 'StringLiteral') {
114
+ const value = (valueNode as any).extra?.rawValue !== undefined ? (valueNode as any).extra.rawValue : valueNode.value;
115
+ if (keyName === 'basePath') callOptions.basePath = value;
116
+ else if (keyName === 'include') callOptions.include = value;
117
+ else if (keyName === 'encoding') callOptions.encoding = value as BufferEncoding;
118
+ } else {
119
+ this.warn(
120
+ `Option "${keyName}" for constSysfsExpr must be a string literal. Found type: ${valueNode.type}`,
121
+ valueNode.loc?.start.index,
122
+ );
123
+ }
124
+ }
125
+ }
126
+ } else if (args.length >= 1 && args[0].type === 'ObjectExpression') {
127
+ const optionsObj = args[0];
128
+ for (const prop of optionsObj.properties) {
129
+ if (prop.type !== 'ObjectProperty') continue;
130
+ let keyName: string | undefined;
131
+ if (prop.key.type === 'Identifier') keyName = prop.key.name;
132
+ else if (prop.key.type === 'StringLiteral') keyName = prop.key.value;
133
+ else continue;
134
+
135
+ // In this case, we need to look for 'basePath' and 'include' within the options object itself
136
+ if (!['basePath', 'include', 'encoding'].includes(keyName)) continue;
137
+
138
+ const valueNode = prop.value;
139
+ if (valueNode.type === 'StringLiteral') {
140
+ const value = (valueNode as any).extra?.rawValue !== undefined ? (valueNode as any).extra.rawValue : valueNode.value;
141
+ if (keyName === 'basePath') callOptions.basePath = value;
142
+ else if (keyName === 'include') callOptions.include = value;
143
+ else if (keyName === 'encoding') callOptions.encoding = value as BufferEncoding;
144
+ } else {
145
+ this.warn(
146
+ `Option "${keyName}" for constSysfsExpr must be a string literal. Found type: ${valueNode.type}`,
147
+ valueNode.loc?.start.index,
148
+ );
149
+ }
150
+ }
151
+
152
+ if (callOptions.include !== '**/*') {
153
+ pathOrPattern = callOptions.include;
154
+ } else {
155
+ if (!callOptions.basePath) {
156
+ this.warn(
157
+ `constSysfsExpr called with only an options object requires at least 'include' or 'basePath' for a pattern.`,
158
+ node.loc?.start.index,
159
+ );
160
+ return;
161
+ }
162
+ this.warn(`constSysfsExpr called with only an options object requires an explicit 'include' pattern.`, node.loc?.start.index);
163
+ return;
164
+ }
165
+ } else {
166
+ this.warn(`constSysfsExpr requires a path/pattern string/variable or an options object as the first argument.`, node.loc?.start.index);
167
+ return;
168
+ }
169
+
170
+ if (!pathOrPattern) {
171
+ this.warn(`Invalid or unresolved path/pattern argument for constSysfsExpr.`, args[0]?.loc?.start.index);
172
+ return;
173
+ }
174
+
175
+ try {
176
+ const currentLocString = node.loc?.start ? ` at ${id}:${node.loc.start.line}:${node.loc.start.column}` : ` in ${id}`;
177
+
178
+ const searchBasePath = callOptions.basePath
179
+ ? path.isAbsolute(callOptions.basePath)
180
+ ? callOptions.basePath
181
+ : path.resolve(path.dirname(id), callOptions.basePath)
182
+ : path.isAbsolute(pathOrPattern) && !/[?*+!@()[\]{}]/.test(pathOrPattern)
183
+ ? path.dirname(pathOrPattern)
184
+ : path.resolve(path.dirname(id), path.dirname(pathOrPattern));
185
+
186
+ let embeddedContent: string;
187
+ let embeddedCount = 0;
188
+
189
+ const isPotentialPattern = /[?*+!@()[\]{}]/.test(pathOrPattern);
190
+
191
+ if (
192
+ !isPotentialPattern &&
193
+ fs.existsSync(path.resolve(searchBasePath, pathOrPattern)) &&
194
+ fs.statSync(path.resolve(searchBasePath, pathOrPattern)).isFile()
195
+ ) {
196
+ const singleFilePath = path.resolve(searchBasePath, pathOrPattern);
197
+ Log(`Mode: Single file (first argument "${pathOrPattern}" resolved to "${singleFilePath}" relative to "${searchBasePath}")`);
198
+
199
+ try {
200
+ const rawContent: string | Buffer = fs.readFileSync(singleFilePath, callOptions.encoding);
201
+ const contentString = rawContent.toString();
202
+ const fileInfo: FileInfo = {
203
+ content: contentString,
204
+ filePath: singleFilePath,
205
+ fileName: path.relative(searchBasePath, singleFilePath),
206
+ };
207
+ embeddedContent = JSON.stringify(fileInfo);
208
+ embeddedCount = 1;
209
+ Log(`Embedded 1 specific file for call${currentLocString}`);
210
+ } catch (fileError: unknown) {
211
+ let message = String(fileError instanceof Error ? fileError.message : fileError ?? 'Unknown file read error');
212
+ this.error(`Error reading file ${singleFilePath}: ${message}`, node.loc?.start.index);
213
+ return;
214
+ }
215
+ } else {
216
+ Log(`Mode: Multi-file (first argument "${pathOrPattern}" is pattern or not a single file)`);
217
+
218
+ Log(`Searching with pattern "${pathOrPattern}" in directory "${searchBasePath}" (encoding: ${callOptions.encoding})`);
219
+
220
+ const matchingFiles = glob.sync(pathOrPattern, {
221
+ cwd: searchBasePath,
222
+ nodir: true,
223
+ absolute: true,
224
+ });
225
+
226
+ const fileInfoArray: FileInfo[] = [];
227
+ for (const fullPath of matchingFiles) {
228
+ try {
229
+ const rawContent: string | Buffer = fs.readFileSync(fullPath, callOptions.encoding);
230
+ const contentString = rawContent.toString();
231
+ fileInfoArray.push({
232
+ content: contentString,
233
+ filePath: fullPath,
234
+ fileName: path.relative(searchBasePath, fullPath),
235
+ });
236
+ } catch (fileError: unknown) {
237
+ let message = String(fileError instanceof Error ? fileError.message : fileError ?? 'Unknown file read error');
238
+ this.warn(`Error reading file ${fullPath}: ${message}`);
239
+ }
240
+ }
241
+ embeddedContent = JSON.stringify(fileInfoArray);
242
+ embeddedCount = fileInfoArray.length;
243
+ Log(`Embedded ${embeddedCount} file(s) matching pattern for call${currentLocString}`);
244
+ }
245
+
246
+ // Replace the call expression with the generated content string
247
+ magicString.overwrite(node.start, node.end, embeddedContent);
248
+ hasReplaced = true;
249
+ } catch (error: unknown) {
250
+ console.error(`Failed to process files for constSysfsExpr call in ${id}:`, error);
251
+ const message = String(error instanceof Error ? error.message : error ?? 'Unknown error during file processing');
252
+ this.error(`Could not process files for constSysfsExpr: ${message}`, node.loc?.start.index);
253
+ return;
254
+ }
255
+ }
256
+ },
257
+ });
258
+ } catch (error: unknown) {
259
+ console.error(`Error parsing or traversing ${id}:`, error);
260
+ const message = String(error instanceof Error ? error.message : error ?? 'Unknown parsing error');
261
+ this.error(`Failed to parse ${id}: ${message}`);
262
+ return null;
263
+ }
264
+
265
+ // If no replacements were made, return null
266
+ if (!hasReplaced) {
267
+ return null;
268
+ }
269
+
270
+ // Return the modified code and source map
271
+ const result: SourceDescription = {
272
+ code: magicString.toString(),
273
+ map: magicString.generateMap({ hires: true }),
274
+ };
275
+ return result;
276
+ },
277
+ };
278
+ }
package/VersionMon.ts CHANGED
@@ -5,24 +5,23 @@ import { dirname } from 'path';
5
5
  import { Logger } from './Logger';
6
6
 
7
7
  export const CheckForUpdates = async (): Promise<boolean> => {
8
- return new Promise<boolean>(async (resolve) => {
9
- const packageJsonPath = path.resolve(dirname(fileURLToPath(import.meta.url)), '../package.json');
10
- const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
11
-
12
- fetch("https://registry.npmjs.org/@steambrew/ttc").then(response => response.json()).then(json => {
8
+ return new Promise<boolean>(async (resolve) => {
9
+ const packageJsonPath = path.resolve(dirname(fileURLToPath(import.meta.url)), '../package.json');
10
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
13
11
 
14
- if (json?.["dist-tags"]?.latest != packageJson.version) {
15
-
16
- Logger.Tree(`@steambrew/ttc@${packageJson.version} requires update to ${json?.["dist-tags"]?.latest}`, {
17
- cmd: `run "npm install @steambrew/ttc@${json?.["dist-tags"]?.latest}" to get latest updates!`
18
- })
12
+ fetch('https://registry.npmjs.org/@steambrew/ttc')
13
+ .then((response) => response.json())
14
+ .then((json) => {
15
+ if (json?.['dist-tags']?.latest != packageJson.version) {
16
+ Logger.Tree('versionMon', `@steambrew/ttc@${packageJson.version} requires update to ${json?.['dist-tags']?.latest}`, {
17
+ cmd: `run "npm install @steambrew/ttc@${json?.['dist-tags']?.latest}" to get latest updates!`,
18
+ });
19
19
 
20
- resolve(true)
21
- }
22
- else {
23
- Logger.Info(`@steambrew/ttc@${packageJson.version} is up-to-date!`)
24
- resolve(false)
25
- }
26
- })
27
- })
28
- }
20
+ resolve(true);
21
+ } else {
22
+ Logger.Info('versionMon', `@steambrew/ttc@${packageJson.version} is up-to-date!`);
23
+ resolve(false);
24
+ }
25
+ });
26
+ });
27
+ };