fazer-lang 2.2.1 → 2.4.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,208 @@
1
+
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { execSync, spawnSync } = require('child_process');
5
+
6
+ function log(msg) { console.log(`[Fazer Build] ${msg}`); }
7
+ function error(msg) { console.error(`[Error] ${msg}`); process.exit(1); }
8
+
9
+ module.exports = async function build(inputFile, args) {
10
+ if (!inputFile) error("No input file specified. Usage: fazer build <app.fz>");
11
+
12
+ const inputPath = path.resolve(inputFile);
13
+ if (!fs.existsSync(inputPath)) error(`Input file not found: ${inputPath}`);
14
+
15
+ const appName = path.basename(inputFile, '.fz');
16
+ const distDir = path.resolve(process.cwd(), 'dist', appName);
17
+
18
+ // Parse args
19
+ let iconPath = null;
20
+ for(let i=0; i<args.length; i++) {
21
+ if (args[i] === '--icon' && args[i+1]) {
22
+ iconPath = path.resolve(args[i+1]);
23
+ i++;
24
+ }
25
+ }
26
+
27
+ log(`Building '${appName}'...`);
28
+ log(`Output Directory: ${distDir}`);
29
+
30
+ // 1. Prepare Directory
31
+ if (fs.existsSync(distDir)) {
32
+ fs.rmSync(distDir, { recursive: true, force: true });
33
+ }
34
+ fs.mkdirSync(distDir, { recursive: true });
35
+
36
+ // 2. Copy Core Files
37
+ const fazerRoot = path.dirname(__dirname); // tools/.. -> fazer-lang/
38
+ const fazerJsPath = path.join(fazerRoot, 'fazer.js');
39
+
40
+ fs.copyFileSync(fazerJsPath, path.join(distDir, 'fazer.js'));
41
+ fs.copyFileSync(inputPath, path.join(distDir, 'app.fz'));
42
+
43
+ // 3. Copy node_modules (Optimized: only copy chevrotain/ws if possible, but full copy is safer)
44
+ const nodeModulesSrc = path.join(fazerRoot, 'node_modules');
45
+ if (fs.existsSync(nodeModulesSrc)) {
46
+ log("Copying dependencies...");
47
+ // Use robocopy on Windows for speed, or recursive copy
48
+ try {
49
+ // Recursive copy
50
+ fs.cpSync(nodeModulesSrc, path.join(distDir, 'node_modules'), { recursive: true });
51
+ } catch(e) {
52
+ log("Warning: Failed to copy node_modules. You may need to run 'npm install' in dist.");
53
+ }
54
+ } else {
55
+ log("Warning: node_modules not found. The app might not run without dependencies.");
56
+ }
57
+
58
+ // 4. Generate Launcher (C#)
59
+ log("Generating Native Launcher...");
60
+
61
+ // We embed the logic to find 'node'
62
+ // If we want to be truly portable, we should copy node.exe here too.
63
+ // For now, we assume 'node' is in PATH or next to the exe.
64
+
65
+ const launcherCs = `
66
+ using System;
67
+ using System.Diagnostics;
68
+ using System.IO;
69
+ using System.Windows.Forms;
70
+
71
+ class Program {
72
+ [STAThread]
73
+ static void Main() {
74
+ string appDir = AppDomain.CurrentDomain.BaseDirectory;
75
+ string script = Path.Combine(appDir, "fazer.js");
76
+ string app = Path.Combine(appDir, "app.fz");
77
+
78
+ string nodeExe = "node";
79
+ if (File.Exists(Path.Combine(appDir, "node.exe"))) {
80
+ nodeExe = Path.Combine(appDir, "node.exe");
81
+ }
82
+
83
+ string extraArgs = "";
84
+ string[] cmdArgs = Environment.GetCommandLineArgs();
85
+ // Skip first arg which is the executable itself
86
+ for (int i = 1; i < cmdArgs.Length; i++) {
87
+ extraArgs += " \\\"" + cmdArgs[i] + "\\\"";
88
+ }
89
+
90
+ ProcessStartInfo psi = new ProcessStartInfo();
91
+ psi.FileName = nodeExe;
92
+ // Arguments: "path/to/fazer.js" "path/to/app.fz" extraArgs
93
+ psi.Arguments = "\\\"" + script + "\\\" \\\"" + app + "\\\" " + extraArgs;
94
+
95
+ psi.UseShellExecute = false;
96
+ psi.CreateNoWindow = true;
97
+ psi.WindowStyle = ProcessWindowStyle.Hidden;
98
+
99
+ try {
100
+ Process p = Process.Start(psi);
101
+ } catch (Exception e) {
102
+ MessageBox.Show("Failed to launch Fazer App:\\n" + e.Message + "\\n\\nEnsure Node.js is installed or node.exe is in the folder.", "Launch Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
103
+ }
104
+ }
105
+ }
106
+ `;
107
+
108
+ const csPath = path.join(distDir, 'Launcher.cs');
109
+ fs.writeFileSync(csPath, launcherCs);
110
+
111
+ // 5. Compile Launcher
112
+ // We use C# compiler (csc.exe) which is available on most Windows
113
+ // Or we use PowerShell Add-Type hack to compile to exe?
114
+ // Add-Type -OutputAssembly is easiest from PS.
115
+
116
+ const exeName = `${appName}.exe`;
117
+ const exePath = path.join(distDir, exeName);
118
+
119
+ let iconArg = "";
120
+ if (iconPath && fs.existsSync(iconPath)) {
121
+ // Copy icon to dist
122
+ const distIcon = path.join(distDir, 'app.ico');
123
+ fs.copyFileSync(iconPath, distIcon);
124
+ iconArg = `-Win32Icon "${distIcon}"`; // PowerShell param
125
+ // For csc: /win32icon:app.ico
126
+ }
127
+
128
+ log("Compiling EXE...");
129
+
130
+ // We use PowerShell to compile because locating csc.exe can be annoying
131
+ // Add-Type -TypeDefinition $code -OutputAssembly $out -Target WinExe ...
132
+
133
+ const psScript = `
134
+ $code = Get-Content -Raw "${csPath}"
135
+ $params = @{
136
+ TypeDefinition = $code
137
+ OutputAssembly = "${exePath}"
138
+ Target = "WinExe"
139
+ ReferencedAssemblies = "System.Windows.Forms"
140
+ }
141
+ ${iconArg ? `$compilerOptions = New-Object System.CodeDom.Compiler.CompilerParameters
142
+ $compilerOptions.CompilerOptions = "/win32icon:${path.join(distDir, 'app.ico').replace(/\\/g, '\\\\')}"
143
+ # Add-Type doesn't easily support CompilerOptions for icons in all versions.
144
+ # Fallback to direct csc call if needed or use specific Add-Type overload.
145
+ ` : ''}
146
+
147
+ # Simple compilation without icon for now via Add-Type,
148
+ # but for Icon we usually need csc. Let's try to find csc.
149
+
150
+ $csc = (Get-ChildItem -Path "$env:windir\\Microsoft.NET\\Framework64\\v4*" -Filter csc.exe | Select-Object -Last 1).FullName
151
+ if (-not $csc) {
152
+ $csc = (Get-ChildItem -Path "$env:windir\\Microsoft.NET\\Framework\\v4*" -Filter csc.exe | Select-Object -Last 1).FullName
153
+ }
154
+
155
+ if ($csc) {
156
+ Write-Host "Compiling with CSC: $csc"
157
+ $args = @("/target:winexe", "/out:${exePath}", "${csPath}")
158
+ if ("${iconArg}") { $args += "/win32icon:${path.join(distDir, 'app.ico')}" }
159
+ & $csc $args
160
+ } else {
161
+ Write-Host "CSC not found, using CSharpCodeProvider..."
162
+ $codeProvider = New-Object Microsoft.CSharp.CSharpCodeProvider
163
+ $parameters = New-Object System.CodeDom.Compiler.CompilerParameters
164
+ $parameters.GenerateExecutable = $true
165
+ $parameters.OutputAssembly = "${exePath}"
166
+ $parameters.CompilerOptions = "/target:winexe"
167
+ $parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll")
168
+ $parameters.ReferencedAssemblies.Add("System.dll")
169
+ $parameters.ReferencedAssemblies.Add("System.Drawing.dll")
170
+
171
+ if ("${iconArg}") {
172
+ $parameters.CompilerOptions += " /win32icon:${path.join(distDir, 'app.ico').replace(/\\/g, '\\\\')}"
173
+ }
174
+
175
+ $results = $codeProvider.CompileAssemblyFromSource($parameters, $code)
176
+
177
+ if ($results.Errors.HasErrors) {
178
+ foreach($e in $results.Errors) {
179
+ Write-Error $e.ToString()
180
+ }
181
+ exit 1
182
+ }
183
+ }
184
+ `;
185
+
186
+ // Write PS build script
187
+ const psBuildPath = path.join(distDir, 'build_exe.ps1');
188
+ fs.writeFileSync(psBuildPath, psScript);
189
+
190
+ try {
191
+ execSync(`powershell -ExecutionPolicy Bypass -File "${psBuildPath}"`, { stdio: 'inherit' });
192
+ } catch(e) {
193
+ log("Compilation failed. See error above.");
194
+ // Clean up temp files
195
+ return;
196
+ }
197
+
198
+ // Clean up
199
+ if (fs.existsSync(exePath)) {
200
+ fs.unlinkSync(csPath);
201
+ fs.unlinkSync(psBuildPath);
202
+ log("Build Success!");
203
+ log(`Created: ${exePath}`);
204
+ log("You can now zip the folder '${distDir}' and share it.");
205
+ } else {
206
+ error("EXE file was not created.");
207
+ }
208
+ };