diff --git a/packages/webgl-jsbuild/upm/Editor/WebGLPostProcessor.cs b/packages/webgl-jsbuild/upm/Editor/WebGLPostProcessor.cs index c91d137..1e16b5a 100644 --- a/packages/webgl-jsbuild/upm/Editor/WebGLPostProcessor.cs +++ b/packages/webgl-jsbuild/upm/Editor/WebGLPostProcessor.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Collections.Generic; +using System.Linq; /** * !!!!!!!!!!必读!!!!!!! @@ -22,37 +23,39 @@ * 如果你的游戏里使用了自定义的Loader,那么我建议您自己重新实现一个PostProcessor,以和你的Loader配套。 */ -public class WebGLPuertsPostProcessor { +public class WebGLPuertsPostProcessor +{ - public static List fileGlobbers = new List + public static List fileGlobbers = new List { Application.dataPath + "/**/Resources/**/*.mjs", Application.dataPath + "/**/Resources/**/*.cjs", Path.GetFullPath("Packages/com.tencent.puerts.core/") + "/**/Resources/**/*.mjs" }; - private static void run(string runEntry, string lastBuiltPath) + private static void RunBuild(string runEntry, string lastBuiltPath) { string PuertsWebglJSRoot = Path.GetFullPath("Packages/com.tencent.puerts.webgl.jsbuild/Javascripts~/"); - if (lastBuiltPath != null) { + if (lastBuiltPath != null) + { UnityEngine.Debug.Log("上一次构建路径:" + lastBuiltPath); - } else { + } + else + { lastBuiltPath = PuertsWebglJSRoot + "dist/"; UnityEngine.Debug.Log("上一次构建路径为空,生成至:" + lastBuiltPath); } - JsEnv jsenv = new JsEnv(); - jsenv.UsingAction(); jsenv.UsingAction(); - jsenv.UsingAction(); + jsenv.UsingAction(); jsenv.Eval>(@"(function (requirePath) { global.require = require('node:module').createRequire(requirePath) })")(PuertsWebglJSRoot); - + Action cpRuntimeJS = jsenv.Eval>(@" (function(puerRuntimeFilePath, targetPath) { const { cp } = require('@puerts/shell-util'); @@ -61,24 +64,55 @@ private static void run(string runEntry, string lastBuiltPath) }); "); - Action globjs = jsenv.Eval>(@" - (function(csFileGlobbers, targetPath) { - const fileGlobbers = []; - for (let i = 0; i < csFileGlobbers.Length; i++) { - fileGlobbers.push(csFileGlobbers.get_Item(i)); + Action globjs = jsenv.Eval>(@" + (function(buildParameters) { + function toJsArray(csArray) { + if (!csArray) + return null; + let results = []; + for (let i = 0; i < csArray.Length; i++) { + results.push(csArray.get_Item(i)); + } + return results; } - const globAllJS = require('.'); + function toJsFunction(csDelegate) { + if (!csDelegate) + return null; + if (typeof (csDelegate) === 'function') + return csDelegate; + if (typeof (csDelegate.Invoke) === 'function') { + return function () { + return csDelegate.Invoke(...arguments); + } + } + return null; + } + let _buildParameters = { + outputpath: buildParameters.outputpath, + fileGlobbers: toJsArray(buildParameters.fileGlobbers), + customScripts: toJsArray(buildParameters.customScripts), + filterPath: toJsFunction(buildParameters.filterPath), + resolvePath: toJsFunction(buildParameters.resolvePath), + }; - globAllJS." + runEntry + @"(fileGlobbers, targetPath); - + const globAllJS = require('.'); + globAllJS." + runEntry + @"(_buildParameters); }); "); - - try { + + try + { cpRuntimeJS(Path.GetFullPath("Packages/com.tencent.puerts.webgl/Javascripts~/PuertsDLLMock/dist/puerts-runtime.js"), lastBuiltPath); - globjs(fileGlobbers.ToArray(), lastBuiltPath); - } - catch(Exception e) + globjs(new BuildParameters() + { + outputpath = lastBuiltPath, + fileGlobbers = fileGlobbers.ToArray(), + customScripts = CustomScript.From(WebGLUtils.GetBuildinScriptsFromConfigure()), + filterPath = WebGLUtils.GetBuildinFilterFunction(), + resolvePath = WebGLUtils.GetBuildinResolvePathFunction(), + }); + } + catch (Exception e) { jsenv.Dispose(); throw; @@ -87,23 +121,23 @@ private static void run(string runEntry, string lastBuiltPath) } [MenuItem("Tools/PuerTS/WebGL/build puerts-js for minigame", false, 11)] - public static void minigame() + public static void BuildMinigame() { - run("buildForMinigame", GetLastBuildPath() != null ? GetLastBuildPath() + "/../minigame" : null); + RunBuild("buildForMinigame", GetLastBuildPath() != null ? GetLastBuildPath() + "/../minigame" : null); } - [MenuItem("Tools/PuerTS/WebGL/build puerts-js for browser", false, 11)] - public static void browser() + public static void BuildBrowser() { - run("buildForBrowser", GetLastBuildPath()); - } + RunBuild("buildForBrowser", GetLastBuildPath()); + } + [MenuItem("Tools/PuerTS/WebGL/install", false, 0)] - static void npmInstall() + static void NodeModulesInstall() { JsEnv jsenv = new Puerts.JsEnv(); jsenv.UsingAction(); - try + try { Action npmInstaller = jsenv.Eval>(@" (function(cwd) { @@ -112,39 +146,66 @@ static void npmInstall() "); npmInstaller(Path.GetFullPath("Packages/com.tencent.puerts.webgl.jsbuild/Javascripts~")); } - catch(Exception e) + catch (Exception e) { UnityEngine.Debug.LogError("Npm install failed. you can cd into 'Packages/com.tencent.puerts.webgl.jsbuild/Javascripts~' to run 'npm install' by yourself"); } } - [MenuItem("Tools/PuerTS/WebGL/build puerts-js for minigame", true)] [MenuItem("Tools/PuerTS/WebGL/build puerts-js for browser", true)] - static bool NodeModulesInstalled() + static bool NodeModulesInstalled() { return Directory.Exists(Path.GetFullPath("Packages/com.tencent.puerts.webgl.jsbuild/Javascripts~/node_modules")); } [MenuItem("Tools/PuerTS/WebGL/install", true)] - static bool NodeModulesNotInstalled() + static bool NodeModulesInstallValidate() { return !NodeModulesInstalled(); } - - protected static string GetLastBuildPath() { + protected static string GetLastBuildPath() + { return EditorPrefs.GetString("PUER_WEBGL_LAST_BUILDPATH"); } [PostProcessBuildAttribute(1)] - public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) { - if (target == BuildTarget.WebGL) + if (target == BuildTarget.WebGL) { EditorPrefs.SetString("PUER_WEBGL_LAST_BUILDPATH", pathToBuiltProject); UnityEngine.Debug.Log("构建成功,请用puerts-webgl/build js构建js资源"); } } -} + + + class CustomScript + { + public string resourceName; + public string code; + public CustomScript(string resourceName, string code) + { + this.resourceName = resourceName; + this.code = code; + } + public static CustomScript[] From(Dictionary dict) + { + if (dict == null) + return null; + return dict.Keys + .Select(resourceName => new CustomScript(resourceName, dict[resourceName])) + .ToArray(); + } + } + class BuildParameters + { + public string outputpath; + public string[] fileGlobbers; + public CustomScript[] customScripts; + public Func filterPath; + public Func resolvePath; + } +} \ No newline at end of file diff --git a/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs b/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs new file mode 100644 index 0000000..05b688b --- /dev/null +++ b/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Codice.Utils; + +public static class WebGLUtils +{ + public static Dictionary Concat(params Dictionary[] dicts) + { + Dictionary results = new Dictionary(); + + foreach (var dict in dicts) + { + if (dict == null) + continue; + foreach (var member in dict) + { + results[member.Key] = member.Value; + } + } + return results; + } + + public delegate string ResolvePathFunction(string path); + public static Func GetBuildinResolvePathFunction() + { + var resolves = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetExportedTypes() + from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + where method.IsDefined(typeof(WebGLResolvePath)) && !method.IsGenericMethod + where method.ReturnType == typeof(string) && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(string) + select (ResolvePathFunction)method.CreateDelegate(typeof(ResolvePathFunction), null); + if (resolves.Count() == 0) + return null; + + return (path) => + { + string result = null; + foreach (var resolve in resolves) + { + string newPath = resolve(path); + if (!string.IsNullOrEmpty(newPath) && newPath != path) + { + result = newPath; + } + } + return !string.IsNullOrEmpty(result) ? result : path; + }; + } + public delegate bool FilterFunction(string path); + public static Func GetBuildinFilterFunction() + { + var filters = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetExportedTypes() + from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + where method.IsDefined(typeof(WebGLResolvePath)) && !method.IsGenericMethod + where method.ReturnType == typeof(bool) && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(string) + select (FilterFunction)method.CreateDelegate(typeof(FilterFunction), null); + if (filters.Count() == 0) + return null; + + return (path) => + { + foreach (var filter in filters) + { + if (!filter(path)) + return false; + } + return true; + }; + } + + public static Dictionary GetBuildinScriptsFromConfigure() + { + var members = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetExportedTypes() + from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + where method.IsDefined(typeof(WebGLScripts)) && !method.IsGenericMethod + where typeof(Dictionary).IsAssignableFrom(method.ReturnType) && method.GetParameters().Length == 0 + select (Dictionary)method.Invoke(null, null); + if (members.Count() == 0) + return null; + + return Concat(members.ToArray()); + } + public static Dictionary GetBuildinScriptsFromOutputPath(string outputRootPath, string extensioName) + { + if (string.IsNullOrEmpty(outputRootPath) || !Directory.Exists(outputRootPath)) + return null; + + Func filter = (string file) => + { + return file != null && (file.EndsWith(".js") || file.EndsWith(".cjs") || file.EndsWith(".mjs")); + }; + + outputRootPath = outputRootPath.Replace("\\", "/"); + if (!outputRootPath.EndsWith("/")) + { + outputRootPath += "/"; + } + + Dictionary results = new Dictionary(); + + foreach (string file in ScanFiles(outputRootPath, filter)) + { + string resourceName = file.Replace("\\", "/").Replace(outputRootPath, ""); + if (!string.IsNullOrEmpty(extensioName) && Path.GetExtension(resourceName) != extensioName) + { + resourceName = resourceName.Substring(0, resourceName.Length - Path.GetExtension(resourceName).Length) + extensioName; + } + + results[resourceName] = System.Text.Encoding.UTF8.GetString(File.ReadAllBytes(file)); + } + return results; + } + static List ScanFiles(string dirPath, Func filter) + { + List files = new List(); + + DirectoryInfo directoryInfo = new DirectoryInfo(dirPath); + + foreach (FileInfo fileInfo in directoryInfo.GetFiles()) + { + if (filter != null && !filter(fileInfo.FullName)) + continue; + files.Add(fileInfo.FullName); + } + foreach (DirectoryInfo dirInfo in directoryInfo.GetDirectories()) + { + List subFiles = ScanFiles(dirInfo.FullName, filter); + if (subFiles != null) files.AddRange(subFiles); + } + + return files; + } +} +/// +/// 允许自定义处理js文件路径 +/// +[AttributeUsageAttribute(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public class WebGLResolvePath : Attribute +{ +} +/// +/// 允许自定义过滤js文件 +/// +[AttributeUsageAttribute(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public class WebGLFilterPath : Attribute +{ +} +/// +/// 允许自定义添加构建内容 +/// +[AttributeUsageAttribute(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public class WebGLScripts : Attribute +{ + +} \ No newline at end of file diff --git a/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs.meta b/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs.meta new file mode 100644 index 0000000..99e77ca --- /dev/null +++ b/packages/webgl-jsbuild/upm/Editor/WebGLUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b112029862841d40a4a1cc8071cab5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/webgl-jsbuild/upm/Javascripts~/index.js b/packages/webgl-jsbuild/upm/Javascripts~/index.js index 542eeb1..360b332 100644 --- a/packages/webgl-jsbuild/upm/Javascripts~/index.js +++ b/packages/webgl-jsbuild/upm/Javascripts~/index.js @@ -11,8 +11,7 @@ const { mkdir } = require('@puerts/shell-util'); function buildForBrowser(allJSFile, outputpath) { const puerts_js_file = {}; - allJSFile.forEach(({ resourceName, jsfile }) => { - const code = fs.readFileSync(jsfile, 'utf-8'); + allJSFile.forEach(({ resourceName, code }) => { puerts_js_file[resourceName] = `(function(exports, require, module, __filename, __dirname) { ${babel.transformSync(code, { cwd: __dirname, @@ -35,12 +34,12 @@ function buildForMinigame(allJSFile, outputpath) { const outputdir = path.join(outputpath, 'puerts_minigame_js_resources'); mkdir('-p', outputdir); - allJSFile.forEach(({ resourceName, jsfile }) => { + allJSFile.forEach(({ resourceName, code }) => { const resourceFilePath = path.join(outputdir, resourceName); mkdir('-p', path.dirname(resourceFilePath)); fs.writeFileSync( !resourceFilePath.endsWith('.js') ? resourceFilePath + ".js" : resourceFilePath, - fs.readFileSync(jsfile, 'utf-8') + code ); }) } @@ -84,34 +83,45 @@ function buildHTML(outputpath) { `) } -function globAllJSFile (fileGlobbers) { - const allJSFile = fileGlobbers - .reduce((retArr, globber)=> { +function globAllJSFile(buildParameters) { + const allJSFile = buildParameters.fileGlobbers + .reduce((retArr, globber) => { return retArr.concat( glob.sync(path.normalize(globber).replace(/\\/g, '/')) ) }, []) .filter(jsfile => { + if (buildParameters.filterPath) { + return buildParameters.filterPath(jsfile); + } return jsfile.indexOf('Editor') == -1 && jsfile.indexOf('node_modules') == -1 }) .map(jsfile => { + let code = fs.readFileSync(jsfile, 'utf-8'); let resourceNameMatcher = jsfile.split('Resources/'); let resourceName = resourceNameMatcher[resourceNameMatcher.length - 1]; resourceName = resourceName.replace(/\.txt$/, ''); + if (typeof (buildParameters.resolvePath) === "function") { + resourceName = buildParameters.resolvePath(resourceName); + } + else if (buildParameters.resolvePath && typeof (buildParameters.resolvePath.Invoke) === "function") { + resourceName = buildParameters.resolvePath.Invoke(resourceName); + } return { resourceName, - jsfile + code, } }) + .concat(buildParameters.customScripts ?? []) return allJSFile; } -exports.buildForMinigame = function (fileGlobbers, outputpath) { +exports.buildForMinigame = function (buildParameters) { - buildForMinigame(globAllJSFile(fileGlobbers), outputpath); + buildForMinigame(globAllJSFile(buildParameters), buildParameters.outputpath); } -exports.buildForBrowser = function (fileGlobbers, outputpath) { - buildForBrowser(globAllJSFile(fileGlobbers), outputpath); +exports.buildForBrowser = function (buildParameters) { + buildForBrowser(globAllJSFile(buildParameters), buildParameters.outputpath); } \ No newline at end of file diff --git a/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs b/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs new file mode 100644 index 0000000..e5539a8 --- /dev/null +++ b/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using UnityEngine; + +public static class WebGLConfigure +{ + [WebGLFilterPath] + static bool Filter(string jsfile) + { + return !jsfile.Contains("Editor") && !jsfile.Contains("node_modules"); + } + [WebGLResolvePath] + static string Resolve(string jsfile) + { + Debug.Log("Buildin: " + jsfile); + return jsfile; + } + /// + /// 允许自定义添加构建内容: 此时可以扫描目录获取脚本列表 + /// + /// + [WebGLScripts] + static Dictionary CustomScripts() + { + return new Dictionary() + { + {"customScript1.mjs", "console.log('constom script')"} + }; + } +} diff --git a/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs.meta b/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs.meta new file mode 100644 index 0000000..20a889b --- /dev/null +++ b/projects/1_Rotate/Assets/Example/Editor/WebGLConfigure.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90c74742399e6914abd6ffba7d3a7746 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: