@steambrew/ttc 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.
package/Compiler.ts ADDED
@@ -0,0 +1,245 @@
1
+ import { OutputOptions, RollupOptions, rollup } from "rollup";
2
+ import json from '@rollup/plugin-json';
3
+ import commonjs from '@rollup/plugin-commonjs';
4
+ import replace from '@rollup/plugin-replace';
5
+ import typescript from '@rollup/plugin-typescript';
6
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
7
+ import terser from '@rollup/plugin-terser';
8
+
9
+ import chalk from 'chalk'
10
+ import { Logger } from "./Logger";
11
+ import fs from 'fs';
12
+
13
+ declare global {
14
+ interface Window {
15
+ PLUGIN_LIST: any
16
+ }
17
+ }
18
+
19
+ declare const pluginName: string, millennium_main: any, MILLENNIUM_BACKEND_IPC: any
20
+
21
+ export interface TranspilerProps {
22
+ bTersePlugin?: boolean,
23
+ strPluginInternalName: string
24
+ }
25
+
26
+ const WrappedCallServerMethod = "const __call_server_method__ = (methodName, kwargs) => Millennium.callServerMethod(pluginName, methodName, kwargs)"
27
+ const WrappedCallable = "const __wrapped_callable__ = (route) => MILLENNIUM_API.callable(__call_server_method__, route)"
28
+
29
+ /**
30
+ * @description Append the active plugin to the global plugin
31
+ * list and notify that the frontend Loaded.
32
+ */
33
+ function ExecutePluginModule() {
34
+ // Assign the plugin on plugin list.
35
+ Object.assign(window.PLUGIN_LIST[pluginName], millennium_main)
36
+ // Run the rolled up plugins default exported function
37
+ millennium_main["default"]();
38
+ MILLENNIUM_BACKEND_IPC.postMessage(1, { pluginName: pluginName })
39
+ }
40
+
41
+ /**
42
+ * @description Append the active plugin to the global plugin
43
+ * list and notify that the frontend Loaded.
44
+ */
45
+ function ExecuteWebkitModule() {
46
+ // Assign the plugin on plugin list.
47
+ Object.assign(window.PLUGIN_LIST[pluginName], millennium_main)
48
+ // Run the rolled up plugins default exported function
49
+ millennium_main["default"]();
50
+ }
51
+
52
+ /**
53
+ * @description Simple bootstrap function that initializes PLUGIN_LIST
54
+ * for current plugin given that is doesnt exist.
55
+ */
56
+ function InitializePlugins() {
57
+ /**
58
+ * This function is called n times depending on n plugin count,
59
+ * Create the plugin list if it wasn't already created
60
+ */
61
+ !window.PLUGIN_LIST && (window.PLUGIN_LIST = {})
62
+
63
+ // initialize a container for the plugin
64
+ if (!window.PLUGIN_LIST[pluginName]) {
65
+ window.PLUGIN_LIST[pluginName] = {};
66
+ }
67
+ }
68
+
69
+ function InsertMillennium(props: TranspilerProps)
70
+ {
71
+ const ContructFunctions = (parts: any) => { return parts.join('\n'); }
72
+
73
+ const generateBundle = (_: unknown, bundle: any) => {
74
+
75
+ for (const fileName in bundle)
76
+ {
77
+ if (bundle[fileName].type != 'chunk') {
78
+ continue
79
+ }
80
+ Logger.Info("Injecting Millennium shims into module... " + chalk.green.bold("okay"))
81
+
82
+ bundle[fileName].code = ContructFunctions([
83
+ `const pluginName = "${props.strPluginInternalName}";`,
84
+ // insert the bootstrap function and call it
85
+ InitializePlugins.toString(), InitializePlugins.name + "()",
86
+ WrappedCallServerMethod,
87
+ WrappedCallable,
88
+ bundle[fileName].code,
89
+ ExecutePluginModule.toString(), ExecutePluginModule.name + "()"
90
+ ])
91
+ }
92
+ }
93
+
94
+ return {
95
+ name: 'add-plugin-main', generateBundle
96
+ };
97
+ }
98
+
99
+ function InsertWebkitMillennium(props: TranspilerProps)
100
+ {
101
+ const ContructFunctions = (parts: any) => { return parts.join('\n'); }
102
+
103
+ const generateBundle = (_: unknown, bundle: any) => {
104
+
105
+ for (const fileName in bundle)
106
+ {
107
+ if (bundle[fileName].type != 'chunk') {
108
+ continue
109
+ }
110
+ Logger.Info("Injecting Millennium shims into webkit module... " + chalk.green.bold("okay"))
111
+
112
+ bundle[fileName].code = ContructFunctions([
113
+ // define the plugin name at the top of the bundle, so it can be used in wrapped functions
114
+ `const pluginName = "${props.strPluginInternalName}";`,
115
+ // insert the bootstrap function and call it
116
+ InitializePlugins.toString(), InitializePlugins.name + "()",
117
+ // TODO
118
+ WrappedCallServerMethod, WrappedCallable, bundle[fileName].code,
119
+ ExecuteWebkitModule.toString(), ExecuteWebkitModule.name + "()"
120
+ ])
121
+ }
122
+ }
123
+
124
+ return {
125
+ name: 'add-plugin-main', generateBundle
126
+ };
127
+ }
128
+
129
+ function GetPluginComponents(props: TranspilerProps) {
130
+ const pluginList = [
131
+ /**
132
+ * @brief resolve millennium, edit the exported bundle to work with millennium
133
+ */
134
+ InsertMillennium(props),
135
+ typescript(), nodeResolve(), commonjs(), json(),
136
+ replace({
137
+ preventAssignment: true,
138
+ 'process.env.NODE_ENV': JSON.stringify('production'),
139
+ // replace callServerMethod with wrapped replacement function.
140
+ 'Millennium.callServerMethod': `__call_server_method__`,
141
+ 'client.callable': `__wrapped_callable__`,
142
+ delimiters: ['', ''],
143
+ 'client.pluginSelf': 'window.PLUGIN_LIST[pluginName]',
144
+ 'client.Millennium.exposeObj(': 'client.Millennium.exposeObj(exports, '
145
+ }),
146
+ ]
147
+
148
+ if (props.bTersePlugin) {
149
+ pluginList.push(terser())
150
+ }
151
+ return pluginList
152
+ }
153
+
154
+ function GetWebkitPluginComponents(props: TranspilerProps) {
155
+ const pluginList = [
156
+ InsertWebkitMillennium(props), typescript(), nodeResolve(), commonjs(), json(),
157
+ replace({
158
+ preventAssignment: true,
159
+ // replace callServerMethod with wrapped replacement function.
160
+ 'Millennium.callServerMethod': `__call_server_method__`,
161
+ 'client.callable': `__wrapped_callable__`,
162
+ delimiters: ['', ''],
163
+ }),
164
+ ]
165
+
166
+ if (props.bTersePlugin) {
167
+ pluginList.push(terser())
168
+ }
169
+ return pluginList
170
+ }
171
+
172
+ const GetFrontEndDirectory = () => {
173
+ const pluginJsonPath = './plugin.json';
174
+
175
+ try {
176
+ const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
177
+ const frontendDirectory = pluginJson?.frontend;
178
+
179
+ return frontendDirectory ? frontendDirectory : "frontend";
180
+ }
181
+ catch (error) {
182
+ return "frontend";
183
+ }
184
+ }
185
+
186
+ export const TranspilerPluginComponent = async (props: TranspilerProps) => {
187
+
188
+ const frontendRollupConfig: RollupOptions = {
189
+ input: `./${GetFrontEndDirectory()}/index.tsx`,
190
+ plugins: GetPluginComponents(props),
191
+ context: 'window',
192
+ external: ['react', 'react-dom', '@steambrew/client'],
193
+ output: {
194
+ name: "millennium_main",
195
+ file: ".millennium/Dist/index.js",
196
+ globals: {
197
+ react: "window.SP_REACT",
198
+ "react-dom": "window.SP_REACTDOM",
199
+ "@steambrew/client": "window.MILLENNIUM_API"
200
+ },
201
+ exports: 'named',
202
+ format: 'iife'
203
+ }
204
+ }
205
+
206
+ const webkitRollupConfig: RollupOptions = {
207
+ input: `./webkit/index.ts`,
208
+ plugins: GetWebkitPluginComponents(props),
209
+ context: 'window',
210
+ external: ['@steambrew/client'],
211
+ output: {
212
+ name: "millennium_main",
213
+ file: ".millennium/Dist/webkit.js",
214
+ exports: 'named',
215
+ format: 'iife',
216
+ globals: {
217
+ "@steambrew/client": "window.MILLENNIUM_API"
218
+ },
219
+ }
220
+ }
221
+
222
+ Logger.Info("Starting build; this may take a few moments...")
223
+ // Load the Rollup configuration file
224
+ try {
225
+ const bundle = await rollup(frontendRollupConfig);
226
+ const outputOptions = frontendRollupConfig.output as OutputOptions;
227
+
228
+ await bundle.write(outputOptions);
229
+
230
+ // check if the webkit file exists
231
+ if (fs.existsSync(`./webkit/index.ts`)) {
232
+ Logger.Info("Compiling webkit module...")
233
+
234
+ const bundle1 = await rollup(webkitRollupConfig);
235
+ const outputOptions1 = webkitRollupConfig.output as OutputOptions;
236
+
237
+ await bundle1.write(outputOptions1);
238
+ }
239
+
240
+ Logger.Info('Build succeeded!', Number((performance.now() - global.PerfStartTime).toFixed(3)), 'ms elapsed.')
241
+ }
242
+ catch (exception) {
243
+ Logger.Error('Build failed!', exception)
244
+ }
245
+ }
package/Linter.ts ADDED
@@ -0,0 +1,44 @@
1
+ import chalk from 'chalk'
2
+ import path from 'path'
3
+ import { existsSync, readFile } from 'fs'
4
+
5
+ export const ValidatePlugin = (target: string): Promise<any> => {
6
+
7
+ return new Promise<any>((resolve, reject) => {
8
+ if (!existsSync(target)) {
9
+ console.error(chalk.red.bold(`\n[-] --target [${target}] `) + chalk.red("is not a valid system path"))
10
+ reject()
11
+ return
12
+ }
13
+
14
+ const pluginModule = path.join(target, "plugin.json")
15
+
16
+ if (!existsSync(pluginModule)) {
17
+ console.error(chalk.red.bold(`\n[-] --target [${target}] `) + chalk.red("is not a valid plugin (missing plugin.json)"))
18
+ reject()
19
+ return
20
+ }
21
+
22
+ readFile(pluginModule, 'utf8', (err, data) => {
23
+ if (err) {
24
+ console.error(chalk.red.bold(`\n[-] couldn't read plugin.json from [${pluginModule}]`))
25
+ reject()
26
+ return
27
+ }
28
+
29
+ try {
30
+ if (!("name" in JSON.parse(data))) {
31
+ console.error(chalk.red.bold(`\n[-] target plugin doesn't contain "name" in plugin.json [${pluginModule}]`))
32
+ reject()
33
+ }
34
+ else {
35
+ resolve(JSON.parse(data))
36
+ }
37
+ }
38
+ catch (parseError) {
39
+ console.error(chalk.red.bold(`\n[-] couldn't parse JSON in plugin.json from [${pluginModule}]`))
40
+ reject()
41
+ }
42
+ });
43
+ })
44
+ }
package/Logger.ts ADDED
@@ -0,0 +1,49 @@
1
+ import chalk from 'chalk'
2
+
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.log(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 }
package/Parameters.ts ADDED
@@ -0,0 +1,72 @@
1
+ import chalk from 'chalk'
2
+ import { Logger } from "./Logger"
3
+
4
+ /***
5
+ * @brief print the parameter list to the stdout
6
+ */
7
+ export const PrintParamHelp = () => {
8
+
9
+ console.log(
10
+ "millennium-ttc parameter list:" +
11
+ "\n\t" + chalk.magenta("--help") + ": display parameter list" +
12
+ "\n\t" + chalk.bold.red("--build") + ": " + chalk.bold.red("(required)") + ": build type [dev, prod] (prod minifies code)" +
13
+ "\n\t" + chalk.magenta("--target") + ": path to plugin, default to cwd"
14
+ );
15
+ }
16
+
17
+ export enum BuildType {
18
+ DevBuild, ProdBuild
19
+ }
20
+
21
+ export interface ParameterProps {
22
+ type: BuildType,
23
+ targetPlugin: string // path
24
+ }
25
+
26
+ export const ValidateParameters = (args: Array<string>): ParameterProps => {
27
+
28
+ let typeProp: BuildType = BuildType.DevBuild, targetProp: string = process.cwd()
29
+
30
+ if (args.includes("--help")) {
31
+ PrintParamHelp()
32
+ process.exit();
33
+ }
34
+
35
+ // startup args are invalid
36
+ if (!args.includes("--build")) {
37
+ Logger.Error("Received invalid arguments...");
38
+ PrintParamHelp();
39
+ process.exit();
40
+ }
41
+
42
+ for (let i = 0; i < args.length; i++)
43
+ {
44
+ if (args[i] === "--build")
45
+ {
46
+ const BuildMode: string = args[i + 1]
47
+
48
+ switch (BuildMode) {
49
+ case "dev": typeProp = BuildType.DevBuild; break
50
+ case "prod": typeProp = BuildType.ProdBuild; break
51
+ default: {
52
+ Logger.Error('--build parameter must be preceded by build type [dev, prod]');
53
+ process.exit();
54
+ }
55
+ }
56
+ }
57
+
58
+ if (args[i] == "--target") {
59
+ if (args[i + 1] === undefined) {
60
+ Logger.Error('--target parameter must be preceded by system path');
61
+ process.exit();
62
+ }
63
+
64
+ targetProp = args[i + 1]
65
+ }
66
+ }
67
+
68
+ return {
69
+ type: typeProp,
70
+ targetPlugin: targetProp
71
+ }
72
+ }
package/VersionMon.ts ADDED
@@ -0,0 +1,28 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { readFile } from 'fs/promises';
4
+ import { dirname } from 'path';
5
+ import { Logger } from './Logger';
6
+
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/millennium-lib").then(response => response.json()).then(json => {
13
+
14
+ if (json?.["dist-tags"]?.latest != packageJson.version) {
15
+
16
+ Logger.Tree(`millennium-lib@${packageJson.version} requires update to ${json?.["dist-tags"]?.latest}`, {
17
+ cmd: "run `npm i millennium-lib` to get latest updates!"
18
+ })
19
+
20
+ resolve(true)
21
+ }
22
+ else {
23
+ Logger.Info(`millennium-lib@${packageJson.version} is up-to-date!`)
24
+ resolve(false)
25
+ }
26
+ })
27
+ })
28
+ }
package/dist/index.js ADDED
@@ -0,0 +1,389 @@
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk';
3
+ import path, { dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { readFile } from 'fs/promises';
6
+ import fs, { existsSync, readFile as readFile$1 } from 'fs';
7
+ import { rollup } from 'rollup';
8
+ import json from '@rollup/plugin-json';
9
+ import commonjs from '@rollup/plugin-commonjs';
10
+ import replace from '@rollup/plugin-replace';
11
+ import typescript from '@rollup/plugin-typescript';
12
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
13
+ import terser from '@rollup/plugin-terser';
14
+ import { performance as performance$1 } from 'perf_hooks';
15
+
16
+ const Logger = {
17
+ Info: (...LogMessage) => {
18
+ console.log(chalk.magenta.bold("[+]"), ...LogMessage);
19
+ },
20
+ Warn: (...LogMessage) => {
21
+ console.log(chalk.yellow.bold("[*]"), ...LogMessage);
22
+ },
23
+ Error: (...LogMessage) => {
24
+ console.log(chalk.red.bold("[-]"), ...LogMessage);
25
+ },
26
+ Tree: (strTitle, LogObject) => {
27
+ console.log(chalk.magenta.bold("[┬]"), strTitle);
28
+ const isLocalPath = (strTestPath) => {
29
+ // Regular expression to match common file path patterns
30
+ const filePathRegex = /^(\/|\.\/|\.\.\/|\w:\/)?([\w-.]+\/)*[\w-.]+\.\w+$/;
31
+ return filePathRegex.test(strTestPath);
32
+ };
33
+ const entries = Object.entries(LogObject);
34
+ const totalEntries = entries.length;
35
+ for (const [index, [key, value]] of entries.entries()) {
36
+ const connector = index === totalEntries - 1 ? "└" : "├";
37
+ let color = chalk.white;
38
+ switch (typeof value) {
39
+ case typeof String(): {
40
+ color = isLocalPath(value) ? chalk.blueBright : chalk.white;
41
+ break;
42
+ }
43
+ case typeof Boolean():
44
+ color = chalk.green;
45
+ break;
46
+ case typeof Number():
47
+ color = chalk.yellow;
48
+ break;
49
+ }
50
+ console.log(chalk.magenta.bold(` ${connector}──${key}:`), color(value));
51
+ }
52
+ }
53
+ };
54
+
55
+ /***
56
+ * @brief print the parameter list to the stdout
57
+ */
58
+ const PrintParamHelp = () => {
59
+ console.log("millennium-ttc parameter list:" +
60
+ "\n\t" + chalk.magenta("--help") + ": display parameter list" +
61
+ "\n\t" + chalk.bold.red("--build") + ": " + chalk.bold.red("(required)") + ": build type [dev, prod] (prod minifies code)" +
62
+ "\n\t" + chalk.magenta("--target") + ": path to plugin, default to cwd");
63
+ };
64
+ var BuildType;
65
+ (function (BuildType) {
66
+ BuildType[BuildType["DevBuild"] = 0] = "DevBuild";
67
+ BuildType[BuildType["ProdBuild"] = 1] = "ProdBuild";
68
+ })(BuildType || (BuildType = {}));
69
+ const ValidateParameters = (args) => {
70
+ let typeProp = BuildType.DevBuild, targetProp = process.cwd();
71
+ if (args.includes("--help")) {
72
+ PrintParamHelp();
73
+ process.exit();
74
+ }
75
+ // startup args are invalid
76
+ if (!args.includes("--build")) {
77
+ Logger.Error("Received invalid arguments...");
78
+ PrintParamHelp();
79
+ process.exit();
80
+ }
81
+ for (let i = 0; i < args.length; i++) {
82
+ if (args[i] === "--build") {
83
+ const BuildMode = args[i + 1];
84
+ switch (BuildMode) {
85
+ case "dev":
86
+ typeProp = BuildType.DevBuild;
87
+ break;
88
+ case "prod":
89
+ typeProp = BuildType.ProdBuild;
90
+ break;
91
+ default: {
92
+ Logger.Error('--build parameter must be preceded by build type [dev, prod]');
93
+ process.exit();
94
+ }
95
+ }
96
+ }
97
+ if (args[i] == "--target") {
98
+ if (args[i + 1] === undefined) {
99
+ Logger.Error('--target parameter must be preceded by system path');
100
+ process.exit();
101
+ }
102
+ targetProp = args[i + 1];
103
+ }
104
+ }
105
+ return {
106
+ type: typeProp,
107
+ targetPlugin: targetProp
108
+ };
109
+ };
110
+
111
+ const CheckForUpdates = async () => {
112
+ return new Promise(async (resolve) => {
113
+ const packageJsonPath = path.resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json');
114
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
115
+ fetch("https://registry.npmjs.org/millennium-lib").then(response => response.json()).then(json => {
116
+ if (json?.["dist-tags"]?.latest != packageJson.version) {
117
+ Logger.Tree(`millennium-lib@${packageJson.version} requires update to ${json?.["dist-tags"]?.latest}`, {
118
+ cmd: "run `npm i millennium-lib` to get latest updates!"
119
+ });
120
+ resolve(true);
121
+ }
122
+ else {
123
+ Logger.Info(`millennium-lib@${packageJson.version} is up-to-date!`);
124
+ resolve(false);
125
+ }
126
+ });
127
+ });
128
+ };
129
+
130
+ const ValidatePlugin = (target) => {
131
+ return new Promise((resolve, reject) => {
132
+ if (!existsSync(target)) {
133
+ console.error(chalk.red.bold(`\n[-] --target [${target}] `) + chalk.red("is not a valid system path"));
134
+ reject();
135
+ return;
136
+ }
137
+ const pluginModule = path.join(target, "plugin.json");
138
+ if (!existsSync(pluginModule)) {
139
+ console.error(chalk.red.bold(`\n[-] --target [${target}] `) + chalk.red("is not a valid plugin (missing plugin.json)"));
140
+ reject();
141
+ return;
142
+ }
143
+ readFile$1(pluginModule, 'utf8', (err, data) => {
144
+ if (err) {
145
+ console.error(chalk.red.bold(`\n[-] couldn't read plugin.json from [${pluginModule}]`));
146
+ reject();
147
+ return;
148
+ }
149
+ try {
150
+ if (!("name" in JSON.parse(data))) {
151
+ console.error(chalk.red.bold(`\n[-] target plugin doesn't contain "name" in plugin.json [${pluginModule}]`));
152
+ reject();
153
+ }
154
+ else {
155
+ resolve(JSON.parse(data));
156
+ }
157
+ }
158
+ catch (parseError) {
159
+ console.error(chalk.red.bold(`\n[-] couldn't parse JSON in plugin.json from [${pluginModule}]`));
160
+ reject();
161
+ }
162
+ });
163
+ });
164
+ };
165
+
166
+ const WrappedCallServerMethod = "const __call_server_method__ = (methodName, kwargs) => Millennium.callServerMethod(pluginName, methodName, kwargs)";
167
+ const WrappedCallable = "const __wrapped_callable__ = (route) => MILLENNIUM_API.callable(__call_server_method__, route)";
168
+ /**
169
+ * @description Append the active plugin to the global plugin
170
+ * list and notify that the frontend Loaded.
171
+ */
172
+ function ExecutePluginModule() {
173
+ // Assign the plugin on plugin list.
174
+ Object.assign(window.PLUGIN_LIST[pluginName], millennium_main);
175
+ // Run the rolled up plugins default exported function
176
+ millennium_main["default"]();
177
+ MILLENNIUM_BACKEND_IPC.postMessage(1, { pluginName: pluginName });
178
+ }
179
+ /**
180
+ * @description Append the active plugin to the global plugin
181
+ * list and notify that the frontend Loaded.
182
+ */
183
+ function ExecuteWebkitModule() {
184
+ // Assign the plugin on plugin list.
185
+ Object.assign(window.PLUGIN_LIST[pluginName], millennium_main);
186
+ // Run the rolled up plugins default exported function
187
+ millennium_main["default"]();
188
+ }
189
+ /**
190
+ * @description Simple bootstrap function that initializes PLUGIN_LIST
191
+ * for current plugin given that is doesnt exist.
192
+ */
193
+ function InitializePlugins() {
194
+ /**
195
+ * This function is called n times depending on n plugin count,
196
+ * Create the plugin list if it wasn't already created
197
+ */
198
+ !window.PLUGIN_LIST && (window.PLUGIN_LIST = {});
199
+ // initialize a container for the plugin
200
+ if (!window.PLUGIN_LIST[pluginName]) {
201
+ window.PLUGIN_LIST[pluginName] = {};
202
+ }
203
+ }
204
+ function InsertMillennium(props) {
205
+ const ContructFunctions = (parts) => { return parts.join('\n'); };
206
+ const generateBundle = (_, bundle) => {
207
+ for (const fileName in bundle) {
208
+ if (bundle[fileName].type != 'chunk') {
209
+ continue;
210
+ }
211
+ Logger.Info("Injecting Millennium shims into module... " + chalk.green.bold("okay"));
212
+ bundle[fileName].code = ContructFunctions([
213
+ `const pluginName = "${props.strPluginInternalName}";`,
214
+ // insert the bootstrap function and call it
215
+ InitializePlugins.toString(), InitializePlugins.name + "()",
216
+ WrappedCallServerMethod,
217
+ WrappedCallable,
218
+ bundle[fileName].code,
219
+ ExecutePluginModule.toString(), ExecutePluginModule.name + "()"
220
+ ]);
221
+ }
222
+ };
223
+ return {
224
+ name: 'add-plugin-main', generateBundle
225
+ };
226
+ }
227
+ function InsertWebkitMillennium(props) {
228
+ const ContructFunctions = (parts) => { return parts.join('\n'); };
229
+ const generateBundle = (_, bundle) => {
230
+ for (const fileName in bundle) {
231
+ if (bundle[fileName].type != 'chunk') {
232
+ continue;
233
+ }
234
+ Logger.Info("Injecting Millennium shims into webkit module... " + chalk.green.bold("okay"));
235
+ bundle[fileName].code = ContructFunctions([
236
+ // define the plugin name at the top of the bundle, so it can be used in wrapped functions
237
+ `const pluginName = "${props.strPluginInternalName}";`,
238
+ // insert the bootstrap function and call it
239
+ InitializePlugins.toString(), InitializePlugins.name + "()",
240
+ // TODO
241
+ WrappedCallServerMethod, WrappedCallable, bundle[fileName].code,
242
+ ExecuteWebkitModule.toString(), ExecuteWebkitModule.name + "()"
243
+ ]);
244
+ }
245
+ };
246
+ return {
247
+ name: 'add-plugin-main', generateBundle
248
+ };
249
+ }
250
+ function GetPluginComponents(props) {
251
+ const pluginList = [
252
+ /**
253
+ * @brief resolve millennium, edit the exported bundle to work with millennium
254
+ */
255
+ InsertMillennium(props),
256
+ typescript(), nodeResolve(), commonjs(), json(),
257
+ replace({
258
+ preventAssignment: true,
259
+ 'process.env.NODE_ENV': JSON.stringify('production'),
260
+ // replace callServerMethod with wrapped replacement function.
261
+ 'Millennium.callServerMethod': `__call_server_method__`,
262
+ 'client.callable': `__wrapped_callable__`,
263
+ delimiters: ['', ''],
264
+ 'client.pluginSelf': 'window.PLUGIN_LIST[pluginName]',
265
+ 'client.Millennium.exposeObj(': 'client.Millennium.exposeObj(exports, '
266
+ }),
267
+ ];
268
+ if (props.bTersePlugin) {
269
+ pluginList.push(terser());
270
+ }
271
+ return pluginList;
272
+ }
273
+ function GetWebkitPluginComponents(props) {
274
+ const pluginList = [
275
+ InsertWebkitMillennium(props), typescript(), nodeResolve(), commonjs(), json(),
276
+ replace({
277
+ preventAssignment: true,
278
+ // replace callServerMethod with wrapped replacement function.
279
+ 'Millennium.callServerMethod': `__call_server_method__`,
280
+ 'client.callable': `__wrapped_callable__`,
281
+ delimiters: ['', ''],
282
+ }),
283
+ ];
284
+ if (props.bTersePlugin) {
285
+ pluginList.push(terser());
286
+ }
287
+ return pluginList;
288
+ }
289
+ const GetFrontEndDirectory = () => {
290
+ const pluginJsonPath = './plugin.json';
291
+ try {
292
+ const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
293
+ const frontendDirectory = pluginJson?.frontend;
294
+ return frontendDirectory ? frontendDirectory : "frontend";
295
+ }
296
+ catch (error) {
297
+ return "frontend";
298
+ }
299
+ };
300
+ const TranspilerPluginComponent = async (props) => {
301
+ const frontendRollupConfig = {
302
+ input: `./${GetFrontEndDirectory()}/index.tsx`,
303
+ plugins: GetPluginComponents(props),
304
+ context: 'window',
305
+ external: ['react', 'react-dom', '@steambrew/client'],
306
+ output: {
307
+ name: "millennium_main",
308
+ file: ".millennium/Dist/index.js",
309
+ globals: {
310
+ react: "window.SP_REACT",
311
+ "react-dom": "window.SP_REACTDOM",
312
+ "@steambrew/client": "window.MILLENNIUM_API"
313
+ },
314
+ exports: 'named',
315
+ format: 'iife'
316
+ }
317
+ };
318
+ const webkitRollupConfig = {
319
+ input: `./webkit/index.ts`,
320
+ plugins: GetWebkitPluginComponents(props),
321
+ context: 'window',
322
+ external: ['@steambrew/client'],
323
+ output: {
324
+ name: "millennium_main",
325
+ file: ".millennium/Dist/webkit.js",
326
+ exports: 'named',
327
+ format: 'iife',
328
+ globals: {
329
+ "@steambrew/client": "window.MILLENNIUM_API"
330
+ },
331
+ }
332
+ };
333
+ Logger.Info("Starting build; this may take a few moments...");
334
+ // Load the Rollup configuration file
335
+ try {
336
+ const bundle = await rollup(frontendRollupConfig);
337
+ const outputOptions = frontendRollupConfig.output;
338
+ await bundle.write(outputOptions);
339
+ // check if the webkit file exists
340
+ if (fs.existsSync(`./webkit/index.ts`)) {
341
+ Logger.Info("Compiling webkit module...");
342
+ const bundle1 = await rollup(webkitRollupConfig);
343
+ const outputOptions1 = webkitRollupConfig.output;
344
+ await bundle1.write(outputOptions1);
345
+ }
346
+ Logger.Info('Build succeeded!', Number((performance.now() - global.PerfStartTime).toFixed(3)), 'ms elapsed.');
347
+ }
348
+ catch (exception) {
349
+ Logger.Error('Build failed!', exception);
350
+ }
351
+ };
352
+
353
+ /**
354
+ * this component serves as:
355
+ * - typescript transpiler
356
+ * - rollup configurator
357
+ */
358
+ const CheckModuleUpdates = async () => {
359
+ return await CheckForUpdates();
360
+ };
361
+ const StartCompilerModule = () => {
362
+ const parameters = ValidateParameters(process.argv.slice(2));
363
+ const bTersePlugin = parameters.type == BuildType.ProdBuild;
364
+ Logger.Tree("Transpiler config: ", {
365
+ target: parameters.targetPlugin,
366
+ build: BuildType[parameters.type],
367
+ minify: bTersePlugin
368
+ });
369
+ ValidatePlugin(parameters.targetPlugin).then((json) => {
370
+ const props = {
371
+ bTersePlugin: bTersePlugin,
372
+ strPluginInternalName: json?.name
373
+ };
374
+ TranspilerPluginComponent(props);
375
+ })
376
+ /**
377
+ * plugin is invalid, we close the proccess as it has already been handled
378
+ */
379
+ .catch(() => {
380
+ process.exit();
381
+ });
382
+ };
383
+ const Initialize = () => {
384
+ global.PerfStartTime = performance$1.now();
385
+ CheckModuleUpdates().then((needsUpdate) => {
386
+ needsUpdate ? process.exit() : StartCompilerModule();
387
+ });
388
+ };
389
+ Initialize();
package/index.ts ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * this component serves as:
5
+ * - typescript transpiler
6
+ * - rollup configurator
7
+ */
8
+ import { BuildType, ValidateParameters } from "./Parameters"
9
+ import { CheckForUpdates } from "./VersionMon"
10
+ import { ValidatePlugin } from './Linter'
11
+ import { TranspilerPluginComponent, TranspilerProps } from './Compiler'
12
+ import { performance } from 'perf_hooks';
13
+ import { Logger } from './Logger'
14
+
15
+ declare global {
16
+ var PerfStartTime: number;
17
+ }
18
+
19
+ const CheckModuleUpdates = async () => {
20
+ return await CheckForUpdates()
21
+ }
22
+
23
+ const StartCompilerModule = () => {
24
+
25
+ const parameters = ValidateParameters( process.argv.slice(2) );
26
+ const bTersePlugin = parameters.type == BuildType.ProdBuild
27
+
28
+ Logger.Tree("Transpiler config: ", {
29
+ target: parameters.targetPlugin,
30
+ build: BuildType[parameters.type],
31
+ minify: bTersePlugin
32
+ })
33
+
34
+ ValidatePlugin(parameters.targetPlugin).then((json: any) => {
35
+
36
+ const props: TranspilerProps = {
37
+ bTersePlugin: bTersePlugin,
38
+ strPluginInternalName: json?.name
39
+ }
40
+
41
+ TranspilerPluginComponent(props)
42
+ })
43
+
44
+ /**
45
+ * plugin is invalid, we close the proccess as it has already been handled
46
+ */
47
+ .catch(() => {
48
+ process.exit()
49
+ })
50
+ }
51
+
52
+ const Initialize = () => {
53
+ global.PerfStartTime = performance.now();
54
+
55
+ CheckModuleUpdates().then((needsUpdate: boolean) => {
56
+ needsUpdate ? process.exit() : StartCompilerModule()
57
+ })
58
+ }
59
+
60
+ Initialize();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@steambrew/ttc",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "bin": {
8
+ "millennium-ttc": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "rollup -c"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "description": "A tiny typescript compiler for Millennium plugins.",
20
+ "dependencies": {
21
+ "@rollup/plugin-commonjs": "^28.0.1",
22
+ "@rollup/plugin-json": "^6.1.0",
23
+ "@rollup/plugin-node-resolve": "^15.3.0",
24
+ "@rollup/plugin-replace": "^6.0.1",
25
+ "@rollup/plugin-terser": "^0.4.4",
26
+ "@rollup/plugin-typescript": "^12.1.1",
27
+ "chalk": "^5.3.0",
28
+ "fs": "^0.0.1-security",
29
+ "rollup": "^4.27.4",
30
+ "tslib": "^2.8.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.1"
34
+ }
35
+ }
@@ -0,0 +1,28 @@
1
+ import commonjs from '@rollup/plugin-commonjs';
2
+ import typescript from '@rollup/plugin-typescript';
3
+ import json from '@rollup/plugin-json';
4
+
5
+ export default {
6
+ input: 'index.ts',
7
+ context: 'window',
8
+ output: {
9
+ file: 'dist/index.js'
10
+ },
11
+ plugins: [commonjs(), typescript(), json()],
12
+ external: [
13
+ "chalk",
14
+ "path",
15
+ "url",
16
+ "fs/promises",
17
+ "fs",
18
+ "rollup",
19
+ "@rollup/plugin-json",
20
+ "@rollup/plugin-commonjs",
21
+ "@rollup/plugin-replace",
22
+ "@rollup/plugin-typescript",
23
+ "@rollup/plugin-node-resolve",
24
+ "rollup-plugin-import-css",
25
+ "@rollup/plugin-terser",
26
+ "perf_hooks",
27
+ ]
28
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dist",
4
+ "module": "ESNext",
5
+ "target": "ES2020",
6
+ "jsx": "react",
7
+ "jsxFactory": "window.SP_REACT.createElement",
8
+ "declaration": false,
9
+ "moduleResolution": "node",
10
+ "noUnusedLocals": true,
11
+ "noUnusedParameters": true,
12
+ "esModuleInterop": true,
13
+ "noImplicitReturns": true,
14
+ "noImplicitThis": true,
15
+ "noImplicitAny": true,
16
+ "strict": true,
17
+ "ignoreDeprecations": "5.0",
18
+ "allowSyntheticDefaultImports": true,
19
+ "skipLibCheck": true
20
+ },
21
+ "include": ["./"],
22
+ "exclude": ["node_modules"]
23
+ }
24
+