From 465b5c9092144d9da49afe4b19509e633a0bf93e Mon Sep 17 00:00:00 2001 From: QiangMouRen <2962051004@qq.com> Date: Sun, 9 Aug 2020 11:54:40 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 106 +------------- README.md | 28 ++++ app/.gitignore | 1 + app/images/icon.ico | Bin 0 -> 106235 bytes app/images/icon.png | Bin 0 -> 716 bytes app/index.html | 131 +++++++++++++++++ app/libs/google-closure-compiler/child.js | 27 ++++ app/libs/google-closure-compiler/index.js | 90 ++++++++++++ app/libs/google-closure-compiler/logger.js | 89 ++++++++++++ app/main.js | 156 +++++++++++++++++++++ app/package.json | 14 ++ app/preload.js | 13 ++ app/renderer.js | 102 ++++++++++++++ package.json | 43 ++++++ 14 files changed, 697 insertions(+), 103 deletions(-) create mode 100644 README.md create mode 100644 app/.gitignore create mode 100644 app/images/icon.ico create mode 100644 app/images/icon.png create mode 100644 app/index.html create mode 100644 app/libs/google-closure-compiler/child.js create mode 100644 app/libs/google-closure-compiler/index.js create mode 100644 app/libs/google-closure-compiler/logger.js create mode 100644 app/main.js create mode 100644 app/package.json create mode 100644 app/preload.js create mode 100644 app/renderer.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 6704566..97111e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,104 +1,4 @@ -# Logs -logs +.vscode *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port +build +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..d783b6f --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# Google Closure Compiler Desktop + +## 介绍 + + **Google Closure Compiler 桌面应用** + +### Google Closure Compiler是什么? + +> The Closure Compiler is a tool for making JavaScript download and run faster. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. + +> Closure Compiler是由Google所发展的一套JavaScript编译器,可以编译JavaScript,产生最佳化(最小化)JavaScript代码。Closure Tools是由Google所发展的一套网页应用程序工具组,可以帮助网页开发者快速发展大型的网页应用程序。Closure Compiler是Closure Tools的其中一项工具,它可以编译JavaScript的代码,经过最佳化处理后,产生高效能且最小化的JavaScript,让网页的加载速度与执行性能都可以提高。除了最佳化之外,Closure Compiler也会检查程序的语法,找出所有可能出问题的地方,协助程序设计者改善程序的质量,降低bugs的发生率。 + +> _更多详细文档和帮助请访问Google官方文档 https://developers.google.com/closure/compiler_ + + +## 软件架构 +软件架构说明 +win32 x64 + +## 安装教程 + +1. 打开Google Closure Compiler Desktop Setup.exe +2. 一直点击下一步,安装程序 +3. 从桌面打开程序 + +## 程序截图 + +![aIoVsS.png](https://images.gitee.com/uploads/images/2020/0808/213934_9269214c_1440556.png) \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/app/images/icon.ico b/app/images/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c292e63d388865258fa64ae277bf2462e6b4d822 GIT binary patch literal 106235 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 + + + + + + + + + +
+

Google Closure Compiler Desktop

+
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ +
+
+
+

2020 © 强某人<2962051004@qq.com>

+
+
+ + + + + + + + \ No newline at end of file diff --git a/app/libs/google-closure-compiler/child.js b/app/libs/google-closure-compiler/child.js new file mode 100644 index 0000000..82183e2 --- /dev/null +++ b/app/libs/google-closure-compiler/child.js @@ -0,0 +1,27 @@ +const jsCompiler = require("./index"); + +process.on("message", ({ src, opt }) => { + ClosureCompiler(src, opt).then((result) => { + process.send({ result }); + }).catch((err) => { + process.send({ err }); + }) +}) + +function ClosureCompiler(src, opt) { + return new Promise((resolve, reject) => { + + let _compiler = new jsCompiler(opt); + _compiler.run([{ src }], (exitCode, stdOut, stdErr) => { + _compiler = null; + if (stdErr) { + reject(stdErr); + return; + } + //compilation complete + resolve(stdOut[0].src); + }); + }); +} + +process.on("uncaughtException", console.log) \ No newline at end of file diff --git a/app/libs/google-closure-compiler/index.js b/app/libs/google-closure-compiler/index.js new file mode 100644 index 0000000..1185339 --- /dev/null +++ b/app/libs/google-closure-compiler/index.js @@ -0,0 +1,90 @@ +/* + * Copyright 2018 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Low level class for calling the closure-compiler-js lib + * + * @author Chad Killingsworth (chadkillingsworth@gmail.com) + */ + +'use strict'; + +// const path = require('path'); +// const contribPath = path.resolve(__dirname, 'contrib'); +const jscomp = require('google-closure-compiler-js'); +const CONSOLE_COLOR_CHARS = /\u001B\[\d+m/ug; + +class CompilerJS { + /** @param {Object|Array} flags */ + constructor(flags) { + this.flags = {}; + if (Array.isArray(flags)) { + flags.forEach(flag => { + const flagPargs = flag.split('='); + const normalizedFlag = this.formatArgument(flagPargs[0], flagPargs[1]); + this.flags[normalizedFlag.key] = normalizedFlag.val; + }); + } else { + for (let key in flags) { + const normalizedFlag = this.formatArgument(key, flags[key]); + this.flags[normalizedFlag.key] = normalizedFlag.val; + } + } + } + + /** + * @param {!Array} fileList + * @param {function(number, Array<{src: string, path: string, sourceMap: (string|undefined)}>, string)=} callback + * @return {child_process.ChildProcess} + */ + run(fileList, callback) { + const out = jscomp(this.flags, fileList); + // GWT error and warnings are not true JS arrays, but are array-like. + // Convert them to standard JS arrays. + out.warnings = [].slice.call(out.warnings); + out.errors = [].slice.call(out.errors); + if (callback) { + const errors = []; + const logErrors = require('./logger'); + logErrors(out, fileList, logOutput => { + // The logger uses terminal color markers which we don't want by default. + errors.push(logOutput.replace(CONSOLE_COLOR_CHARS, '')); + }); + callback(errors.length === 0 ? 0 : 1, out.compiledFiles, errors.join('\n\n')); + } + return out; + } + + /** + * @param {string} key + * @param {(string|boolean)=} val + * @return {{key: string, val: (string|undefined)}} + */ + formatArgument(key, val) { + let normalizedKey = key.replace(/_(\w)/g, match => match[1].toUpperCase()); + normalizedKey = normalizedKey.replace(/^--/, ''); + + return { + key: normalizedKey, + val: val === undefined || val === null ? true : val + }; + } +} + +/** @type {string} */ +// CompilerJS.CONTRIB_PATH = contribPath; + +module.exports = CompilerJS; diff --git a/app/libs/google-closure-compiler/logger.js b/app/libs/google-closure-compiler/logger.js new file mode 100644 index 0000000..0c0dbed --- /dev/null +++ b/app/libs/google-closure-compiler/logger.js @@ -0,0 +1,89 @@ +/* + * Copyright 2016 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Logger for Closure Compiler JS output. + */ + +'use strict'; + +const ESC = '\u001B'; +const COLOR_END = ESC + '[0m'; +const COLOR_RED = ESC + '[91m'; +const COLOR_GREEN = ESC + '[92m'; +const COLOR_YELLOW = ESC + '[93m'; +const fs = require('fs'); + +/** + * @param {string} line to generate prefix for + * @param {number} charNo to generate prefix at + * @return {string} prefix for showing a caret + */ +function caretPrefix(line, charNo) { + return line.substr(0, charNo).replace(/[^\t]/g, ' '); +} + + +/** + * @param {!Object} output + * @param {!Array<{src:string, path:string}>} inputFiles + * @param {function(string)} logger + * @return {boolean} Whether this output should fail a compilation. + */ +module.exports = function(output, inputFiles = [], logger = console.warn) { + // TODO(samthor): If this file has a sourceMap, then follow it back out of the rabbit hole. + function fileFor(file) { + if (!file) { + return null; + } + + const originalFile = inputFiles.find(inputFile => inputFile.path === file); + if (originalFile) { + return originalFile; + } + + try { + return { + path: file, + src: fs.readFileSync(file, 'utf8') + } + } catch (e) { } + return null; + } + + function writemsg(color, msg) { + if (!msg.file && msg.lineNo < 0) { + logger(msg.type); + } else { + logger(`${msg.file}:${msg.lineNo} (${msg.type})`) + } + logger(msg.description); + + const file = fileFor(msg.file); + if (file) { + const lines = file.src.split('\n'); // TODO(samthor): cache this for logger? + const line = lines[msg.lineNo - 1] || ''; + logger(color + line + COLOR_END); + logger(COLOR_GREEN + caretPrefix(line, msg.charNo) + '^' + COLOR_END); + } + logger(''); + } + + output.warnings.forEach(writemsg.bind(null, COLOR_YELLOW)); + output.errors.forEach(writemsg.bind(null, COLOR_RED)); + + return output.errors.length > 0; +}; diff --git a/app/main.js b/app/main.js new file mode 100644 index 0000000..d1bc929 --- /dev/null +++ b/app/main.js @@ -0,0 +1,156 @@ +/* + * @Author: 强某人 + * @Date: 2020-08-08 20:45:33 + */ + +const { app, BrowserWindow, Menu, shell } = require('electron'); +const path = require('path'); +let mainWindow = null; + +const windowOpt = { + width: 1200, + height: 800, + // maxWidth: 800, + // maxHeight: 600, + // resizable: false, + frame: false, + + title: "Google Closure Compiler Desktop", + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: true, + } +}; +async function createWindow() { + // Create the browser window. + // await new Promise((resolve) => setTimeout(resolve, 2000)); + mainWindow = new BrowserWindow({ + ...windowOpt + }); + + // loadingwindow.hide(); + + mainWindow.loadFile('index.html'); + mainWindow.on('closed', function () { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + mainWindow = null; + process.exit() + }) + // Open the DevTools. + // mainWindow.webContents.openDevTools() +} + +// app.on("ready", () => { + +// loadingwindow = new BrowserWindow({ +// ...windowOpt +// }) + +// loadingwindow.loadFile('loading.html') // To load the activity loader html file +// loadingwindow.show(); + +// }) + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.whenReady().then(async () => { + await createWindow(); + await setApplicationMenu(); + + app.on('activate', async () => { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) await createWindow() + }) +}) + +// Quit when all windows are closed, except on macOS. There, it's common +// for applications and their menu bar to stay active until the user quits +// explicitly with Cmd + Q. +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit() +}) + +// In this file you can include the rest of your app's specific main process +// code. You can also put them in separate files and require them here. + + + +async function setApplicationMenu() { + const template = [ + { + label: '窗口', + role: 'window', + submenu: [ + { + label: '最小化', + accelerator: 'CmdOrCtrl+M', + role: 'minimize' + }, + { + label: '关闭', + accelerator: 'CmdOrCtrl+W', + role: 'close' + }, + ] + }, + { + label: '视图', + submenu: [ + { + label: '刷新', + accelerator: 'CmdOrCtrl+R', + click(item, focusedWindow) { + if (focusedWindow) + focusedWindow.reload(); + } + }, + { + label: '调试', + accelerator: (() => { + if (process.platform == 'darwin') + return 'Alt+Command+I'; + else + return 'Ctrl+Shift+I'; + })(), + click(item, focusedWindow) { + if (focusedWindow) + focusedWindow.toggleDevTools(); + } + }, + ] + }, + { + label: '帮助', + role: 'help', + submenu: [ + { + label: 'What is the Closure Compiler', + click() { + shell.openExternal('https://developers.google.com/closure/compiler') + } + }, + { + label: 'Closure Compiler Compilation Levels', + click() { + shell.openExternal('https://developers.google.com/closure/compiler/docs/compilation_levels') + } + }, + { + label: 'Author Github Index', + click() { + shell.openExternal('https://github.com/qiangmouren') + } + }, + ] + }, + ]; + + + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); + +} diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..6a4cca1 --- /dev/null +++ b/app/package.json @@ -0,0 +1,14 @@ +{ + "name": "google-closure-compiler-desktop", + "description": "google-closure-compiler-desktop", + "version": "1.0.0", + "main": "main.js", + "devDependencies": { + "electron": "9.1.2" + }, + "dependencies": { + "custom-electron-titlebar": "^3.2.3", + "google-closure-compiler-js": "^20200719.0.0" + }, + "author": "qiangmouren" +} \ No newline at end of file diff --git a/app/preload.js b/app/preload.js new file mode 100644 index 0000000..1dba4cd --- /dev/null +++ b/app/preload.js @@ -0,0 +1,13 @@ +// All of the Node.js APIs are available in the preload process. +// It has the same sandbox as a Chrome extension. +const path = require('path'); +const url = require('url'); +const customTitlebar = require('custom-electron-titlebar'); + +window.addEventListener('DOMContentLoaded', () => { + new customTitlebar.Titlebar({ + backgroundColor: customTitlebar.Color.fromHex('#A0EEE1'), + icon: url.format(path.join(__dirname, '/images', '/icon.png')), + }); + +}) diff --git a/app/renderer.js b/app/renderer.js new file mode 100644 index 0000000..13a46e6 --- /dev/null +++ b/app/renderer.js @@ -0,0 +1,102 @@ +// This file is required by the index.html file and will +// be executed in the renderer process for that window. +// No Node.js APIs are available in this process because +// `nodeIntegration` is turned off. Use `preload.js` to +// selectively enable features needed in the rendering +// process. + +const require = nodeRequire; + +let ClosureCompilerCallback; +const compilerServer = require('child_process').fork( + require.resolve('./libs/google-closure-compiler/child'), + { + // options + } +); +compilerServer.on("message", function (data) { + ClosureCompilerCallback && ClosureCompilerCallback(data) +}) +// const fs = require('fs'); +// const path = require('path'); +// const tempfile = require('tempfile'); + + +function ClosureCompiler(src, opt) { + return new Promise((resolve, reject) => { + + ClosureCompilerCallback = ({ err, result }) => { + ClosureCompilerCallback = null; + if (err) { + reject(err); + return; + } + + console.log(result) + resolve(result) + } + + compilerServer.send({ src, opt }); + }) + +} + +async function ClosureCompilerCode(src, opt) { + + // 多任务 + if (Array.isArray(src)) { + let task = []; + src.forEach((code) => { + task.push(_closureCompilerCode(code, opt)) + }); + return Promise.all(task); + } + if (typeof src != "string") throw new Error("src参数必须为string") + return _closureCompilerCode(src, opt) +} + +async function _closureCompilerCode(src, opt) { + // const tempPath = tempfile(".js"); + // await fs.promises.writeFile(tempPath, src, { encoding: "utf-8" }); + return ClosureCompiler(src, { ...opt })//.finally(() => fs.promises.unlink(tempPath)) +} + +(async () => { + + $("#input-code").attr('placeholder', "// ADD YOUR CODE HERE\nfunction hello(name) {\n alert('Hello, ' + name);\n}\nhello('New user');\n"); + $("#output-wrapper").val("(function(){\n%output%\n}).call(this)"); + + const _submit_text = $('button#submit-compiler').html(); + $("#submit-compiler").click(() => { + const src = $("#input-code").val(); + const compilation_level = $("#compilation-level option:selected").val(); + const warning_level = $("#warning-level option:selected").val(); + const language_in = $("#language-in option:selected").val(); + const language_out = $("#language-out option:selected").val(); + const output_wrapper = $("#output-wrapper").val(); + const opt = { + compilation_level, + warning_level, + language_in, + language_out, + output_wrapper + }; + $('button#submit-compiler').html("Process(处理中)...") + $("select,input,textarea,button#submit-compiler").attr("disabled", true) + ClosureCompilerCode(src, opt) + .then(result => { + $("#output-code").val(result) + }) + .catch(error => { + if (typeof error == 'string') { + $("#output-code").val(error); + return; + } + $("#output-code").val(JSON.stringify(error, Object.getOwnPropertyNames(error), 4)) + }) + .finally(() => { + $('button#submit-compiler').html(_submit_text) + $("select,input,textarea,button#submit-compiler").attr("disabled", false) + }) + }) +})() \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5436506 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "google-closure-compiler-desktop", + "version": "1.0.0", + "description": "Google Closure Compiler Desktop", + "main": "app/main.js", + "scripts": { + "start": "electron app", + "build": "cross-env ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ ELECTRON_CUSTOM_DIR=9.1.2 electron-builder --win --x64" + }, + "build": { + "productName": "Google Closure Compiler Desktop", + "appId": "gcc-desktop", + "copyright": "强某人<2962051004@qq.com>", + "compression": "maximum", + "electronVersion": "9.1.2", + "directories": { + "output": "build" + }, + "asar": true, + "win": { + "icon": "app/images/icon.ico", + "target": "nsis" + }, + "linux": { + "icon": "app/images/icon.ico", + "target": "nsis" + }, + "nsis": { + "allowToChangeInstallationDirectory": true, + "oneClick": false, + "menuCategory": true, + "allowElevation": false + }, + "electronDownload": { + "mirror": "http://npm.taobao.org/mirrors/electron/" + } + }, + "devDependencies": { + "cross-env": "^7.0.2", + "electron": "9.1.2", + "electron-builder": "^22.8.0" + } +} \ No newline at end of file