diff --git a/.mocharc.js b/.mocharc.js index 3677653..027aacc 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -4,6 +4,7 @@ module.exports = { 'ts-node/register' ], recursive: true, - spec: "./test/spec/**/*.spec.ts", + // spec: "./test/spec/**/*.spec.ts", + spec: "test/spec/wcc/module/module.spec.ts", timeout: 20000, } \ No newline at end of file diff --git a/src/module/config/wcc.cc b/src/module/config/wcc.cc index 04cc65b..beac0e4 100644 --- a/src/module/config/wcc.cc +++ b/src/module/config/wcc.cc @@ -3,7 +3,6 @@ namespace wcc_options { using v8::Local; - using v8::NewStringType; using v8::String; using v8::Value; diff --git a/src/module/include/wcc.hh b/src/module/include/wcc.hh index 41d318b..dfc5f7a 100644 --- a/src/module/include/wcc.hh +++ b/src/module/include/wcc.hh @@ -26,5 +26,8 @@ struct WCCOptions std::string lazyloadConfig; }; -bool parse_wcc_options(v8::Isolate *isolate, v8::Local &src, WCCOptions *result); +namespace wcc_options +{ + bool parse_wcc_options(v8::Isolate *isolate, v8::Local &src, WCCOptions *result); +} #endif \ No newline at end of file diff --git a/src/module/wcc.cpp b/src/module/wcc.cpp index f2b853a..7e681f5 100644 --- a/src/module/wcc.cpp +++ b/src/module/wcc.cpp @@ -1,73 +1,323 @@ // hello.cc -#include -#include "../include/wxml.h" #include "./include/wcc.hh" +#include "../include/file.h" +#include "../include/string_utils.h" +#include "../include/wxml.h" +#include "v8.h" +#include +#include +#include -namespace wx_compiler -{ +namespace wx_compiler { - using v8::Function; - using v8::FunctionCallbackInfo; - using v8::FunctionTemplate; - using v8::Isolate; - using v8::Local; - using v8::NewStringType; - using v8::Object; - using v8::String; - using v8::Value; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Isolate; +using v8::Local; +using v8::NewStringType; +using v8::Object; +using v8::String; +using v8::Value; +using std::map; +using std::string; +using std::vector; - void Compiler(const FunctionCallbackInfo &args) - { - Isolate *isolate = args.GetIsolate(); - v8::HandleScope scope(isolate); // Ensure we have a proper handle scope. +int compile(Isolate *isolate, WCCOptions &options, Local &result, + std::string &errMsg) { + int mark = 0; + if (options.debug) { + mark |= 2u; + } + if (options.debugWXS) { + mark |= 4u; + } + std::map fileContentMap; + for (int i = 0; i < options.files.size(); i++) { + fileContentMap[options.files[i]] = options.contents[i]; + } + map> componentListMap; + + std::string xc_Or_completeCode_Param = options.wxmlCompileConfig; + if (!xc_Or_completeCode_Param.empty()) + { + string data; + vector allComponentList; + data = getNextArg(xc_Or_completeCode_Param, options.wxmlCompileConfigSplit); + unsigned long long count = strtoull(&data[0], 0, 10); + for (unsigned long long i = 0; i < count; i++) + { + vector componentList; + string arg1 = getNextArg(xc_Or_completeCode_Param, options.wxmlCompileConfigSplit); + data = getNextArg(xc_Or_completeCode_Param, options.wxmlCompileConfigSplit); + unsigned long long jCount = strtoull(&data[0], 0, 10); + for (unsigned long long i = 0; i < jCount; i++) + { + string componentName = getNextArg(xc_Or_completeCode_Param, options.wxmlCompileConfigSplit); + componentList.push_back(componentName); + auto it = std::find(allComponentList.begin(), allComponentList.end(), componentName); + if (it == allComponentList.end()) + { + allComponentList.push_back(componentName); + } + } + auto it = componentListMap.lower_bound(arg1); + if (it == componentListMap.end() || arg1 < it->first) + { + componentListMap.emplace(arg1, componentList); + } + } + componentListMap["ALL"] = allComponentList; + } - // Check if the first argument is an object. - if (args.Length() < 1 || !args[0]->IsObject()) - { - isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be an object", NewStringType::kNormal).ToLocalChecked()); - return; - } - - // Cast the first argument to an Object. - Local context = isolate->GetCurrentContext(); - Local obj = args[0]->ToObject(context).ToLocalChecked(); - - WCCOptions options; - if(!parse_wcc_options(isolate, obj, &options)) - { - // 选项解析失败 - return; - } - - // TODO: 解析成功,开始编译 - - // Convert the "msg" property to a C++ string and return it. - // String::Utf8Value utf8(isolate, value); - // args.GetReturnValue().Set(String::NewFromUtf8(isolate, *utf8, NewStringType::kNormal).ToLocalChecked()); + if (options.lazyload) { + // 懒加载 + std::vector splitedData; + split(splitedData, options.lazyloadConfig, options.wxmlCompileConfigSplit); + // 处理文件路径 + for (int i = 0; i < splitedData.size(); i++) { + string path = splitedData[i]; + if (path[0] == '.' && path[1] == '/') { + // 以"./"开头,去掉前两个字符 + splitedData[i] = path.substr(2); + } } - void Initialize(Local exports, Local module) - { + int compilerResult = 0; - Isolate *isolate = exports->GetIsolate(); - auto context = isolate->GetCurrentContext(); + std::map outputContentMap; + map outputFuncMap; + map> dependencyListMap; + map mapData1; + const char off_5403C3[] = {'s', '\0', 'e', '\0'}; - std::string versionInfo; - WXML::Compiler::GetVersionInfo(versionInfo, "global"); + compilerResult = WXML::Compiler::CompileLazy( + fileContentMap, errMsg, outputContentMap, + outputFuncMap, // map + dependencyListMap, // std::map> + componentListMap, // componentListMap + splitedData, mapData1, + true, // isLLA, + options.genfuncname, mark, 10, &off_5403C3[2], off_5403C3, "gg", "e_", + "d_", "p_", "\0", "boxofchocolate", "$gdwx", "f_"); + if (outputContentMap.count("__COMMON__") == 0) { + string helperCode; + WXML::Compiler::WXMLHelperCode(helperCode); + string data = + "var __wxAppData=__wxAppData||{};var " + "__wxAppCode__=__wxAppCode__||{};var global=global||{};var " + "__WXML_GLOBAL__=" + "__WXML_GLOBAL__||{entrys:{},defines:{},modules:{},ops:[],wxs_nf_" + "init:undefined,total_ops:0};var Component=Comp" + "onent||function(){};var definePlugin=definePlugin||function(){};var " + "requirePlugin=requirePlugin||function(){};" + "var Behavior=Behavior||function(){};var " + "__vd_version_info__=__vd_version_info__||{};var " + "__GWX_GLOBAL__=__GWX_G" + "LOBAL__||{};var __globalThis=(typeof " + "__vd_version_info__!=='undefined'&&typeof " + "__vd_version_info__.globalThis!" + "=='undefined')?__vd_version_info__.globalThis:(typeof " + "window!=='undefined'?window:globalThis);"; + data = data + helperCode; + outputContentMap["__COMMON__"] = data; + } else { + string helperCode; + WXML::Compiler::WXMLHelperCode(helperCode); + string commonData = + "var __wxAppData=__wxAppData||{};var " + "__wxAppCode__=__wxAppCode__||{};var global=global||{};var " + "__WXML_GLOBAL__=" + "__WXML_GLOBAL__||{entrys:{},defines:{},modules:{},ops:[],wxs_nf_" + "init:undefined,total_ops:0};var Component=Comp" + "onent||function(){};var definePlugin=definePlugin||function(){};var " + "requirePlugin=requirePlugin||function(){};" + "var Behavior=Behavior||function(){};var " + "__vd_version_info__=__vd_version_info__||{};var " + "__GWX_GLOBAL__=__GWX_G" + "LOBAL__||{};var __globalThis=(typeof " + "__vd_version_info__!=='undefined'&&typeof " + "__vd_version_info__.globalThis!" + "=='undefined')?__vd_version_info__.globalThis:(typeof " + "window!=='undefined'?window:globalThis);"; + commonData = commonData + helperCode; + commonData = commonData.append(outputContentMap["__COMMON__"]); - // Set the module.exports to be a function - Local tpl = FunctionTemplate::New(isolate, Compiler); - Local fn = tpl->GetFunction(context).ToLocalChecked(); + outputContentMap["__COMMON__"] = commonData; + } - // Set the 'version' property on the function - fn->Set(context, String::NewFromUtf8(isolate, "version", NewStringType::kNormal).ToLocalChecked(), - String::NewFromUtf8(isolate, versionInfo.c_str(), NewStringType::kNormal).ToLocalChecked()) + std::stringstream dep; + dep << ";var __WXML_DEP__=__WXML_DEP__||{};"; + // dependencyListMap v121 + for (auto j = dependencyListMap.begin(); j != dependencyListMap.end(); + j++) { + + if (j->second.begin() != j->second.end()) { + dep << "__WXML_DEP__[\""; + dep << j->first; + dep << "\"]=["; + auto list = j->second; + + for (auto k = list.begin(); k != list.end(); k++) { + dep << "\""; + dep << WXML::Rewrite::ToStringCode(*k); + dep << "\","; + } + + dep << "];"; + } + } + std::string v140 = dep.str(); + outputContentMap["__COMMON__"].append(v140); + if (!compilerResult) { + + v8::Local funcContent = v8::Object::New(isolate); + for (auto content : outputContentMap) { + funcContent + ->Set(isolate->GetCurrentContext(), + String::NewFromUtf8(isolate, content.first.c_str(), + v8::NewStringType::kNormal) + .ToLocalChecked(), + String::NewFromUtf8(isolate, content.second.c_str(), + v8::NewStringType::kNormal) + .ToLocalChecked()) .Check(); + } + v8::Local funcName = v8::Object::New(isolate); + for (auto func : outputFuncMap) { + funcName + ->Set(isolate->GetCurrentContext(), + String::NewFromUtf8(isolate, func.first.c_str(), + v8::NewStringType::kNormal) + .ToLocalChecked(), + String::NewFromUtf8(isolate, func.second.c_str(), + v8::NewStringType::kNormal) + .ToLocalChecked()) + .Check(); + } - module->Set(context, String::NewFromUtf8(isolate, "exports", NewStringType::kNormal).ToLocalChecked(), fn).Check(); + // 你可以在这里设置对象的属性和方法 + + // 创建一个实例 + v8::Local object_instance = v8::Object::New(isolate); + auto _ = object_instance->Set(isolate->GetCurrentContext(), + String::NewFromUtf8(isolate, "generateFunctionName", + v8::NewStringType::kNormal) + .ToLocalChecked(), + funcName); + _ = object_instance->Set(isolate->GetCurrentContext(), + String::NewFromUtf8(isolate, + "generateFunctionContent", + v8::NewStringType::kNormal) + .ToLocalChecked(), + funcContent); + + result = object_instance; } + return compilerResult; + } else { + // 普通 - NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) + int compilerResult = 0; + std::map v105; + map> componentListMap; + std::string r; + compilerResult = + WXML::Compiler::Compile(fileContentMap, // a2 + errMsg, // a3 + r, // a4 + componentListMap, // a5 + v105, // a6 + false, // a7 + options.genfuncname, // a8 + mark, // a9 + '\n', // + "e", // off_5403C3[2] + "s", // off_5403C3 + "gg", // "gg" a13 + "e_", // "e_" + "d_", // "d_" + "p_", // "p_" + "", // '\0' + "boxofchocolate", // "boxofchocolate" + "$gdwx", // "$gdwx" + "f_"); // "f_" + result = String::NewFromUtf8(isolate, r.c_str(), NewStringType::kNormal) + .ToLocalChecked(); + return compilerResult; + } + return 0; +} -} // namespace demo \ No newline at end of file +void wcc(const FunctionCallbackInfo &args) { + Isolate *isolate = args.GetIsolate(); + v8::HandleScope scope(isolate); // Ensure we have a proper handle scope. + + // Check if the first argument is an object. + if (args.Length() < 1 || !args[0]->IsObject()) { + isolate->ThrowException(String::NewFromUtf8(isolate, + "Argument must be an object", + NewStringType::kNormal) + .ToLocalChecked()); + return; + } + + // Cast the first argument to an Object. + Local context = isolate->GetCurrentContext(); + Local obj = args[0]->ToObject(context).ToLocalChecked(); + + WCCOptions options; + if (!wcc_options::parse_wcc_options(isolate, obj, &options)) { + // 选项解析失败 + return; + } + + Local result; + std::string errMsg; + int code = compile(isolate, options, result, errMsg); + + // Convert the "msg" property to a C++ string and return it. + if (code) { + // error + args.GetReturnValue().Set( + String::NewFromUtf8(isolate, errMsg.c_str(), NewStringType::kNormal) + .ToLocalChecked()); + } else { + // ok + args.GetReturnValue().Set(result); + } +} + +void Initialize(Local exports, Local module) { + + Isolate *isolate = exports->GetIsolate(); + auto context = isolate->GetCurrentContext(); + + std::string versionInfo; + WXML::Compiler::GetVersionInfo(versionInfo, "global"); + + // Set the module.exports to be a function + Local tpl = FunctionTemplate::New(isolate, wcc); + Local fn = tpl->GetFunction(context).ToLocalChecked(); + + // Set the 'version' property on the function + fn->Set(context, + String::NewFromUtf8(isolate, "version", NewStringType::kNormal) + .ToLocalChecked(), + String::NewFromUtf8(isolate, versionInfo.c_str(), + NewStringType::kNormal) + .ToLocalChecked()) + .Check(); + + module + ->Set(context, + String::NewFromUtf8(isolate, "exports", NewStringType::kNormal) + .ToLocalChecked(), + fn) + .Check(); +} + +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) + +} // namespace wx_compiler \ No newline at end of file diff --git a/src/wcc.cpp b/src/wcc.cpp index c204d48..b5b5943 100644 --- a/src/wcc.cpp +++ b/src/wcc.cpp @@ -1,9 +1,7 @@ #include #include #include -#include #include -#include #include "include/file.h" #include "include/usage.h" #include "include/string_utils.h" @@ -64,7 +62,7 @@ int main(int argc, const char **argv) int mark = 0; bool isReadFromStdin = false; bool version = false; - bool v53 = false; + bool isLLA = false; bool hasLL = false; string xc_Or_completeCode_Param; string outputFileName; @@ -88,15 +86,16 @@ int main(int argc, const char **argv) switch (param[1]) { case 'd': - // output code for debug /* code */ if (!param[2]) { + // output code for debug mark |= 2u; continue; } if (param[2] == 's') { + // insert debug wxs info mark |= 4u; continue; } @@ -130,7 +129,7 @@ int main(int argc, const char **argv) /* code */ if (param[2] == 'c' && i + 1 < paramList.size()) { - v53 = true; + isLLA = true; if (paramList[i + 1][0] != '-') { xc_Or_completeCode_Param.assign(paramList[i + 1]); @@ -144,7 +143,7 @@ int main(int argc, const char **argv) /* code */ if (param[2] == 'c' && i + 1 < paramList.size()) { - v53 = false; + isLLA = false; if (paramList[i + 1][0] != '-') { xc_Or_completeCode_Param.assign(paramList[i + 1]); @@ -242,7 +241,7 @@ int main(int argc, const char **argv) printf("Error: expected -llw or -lla, but got %s\n", param.c_str()); return -1; } - v53 = true; + isLLA = true; } string splitMark; if (!splitMarkStr.compare(" ")) @@ -370,7 +369,7 @@ int main(int argc, const char **argv) componentListMap, // componentListMap splitedData, mapData1, - v53, + isLLA, gwxMark, mark, 10, @@ -467,11 +466,11 @@ int main(int argc, const char **argv) compilerResult = WXML::Compiler::Compile( fileContentMap, // a2 - v111, // a3 + errorMessage, // a3 v113, // a4 componentListMap, // a5 v105, // a6 - v53, // a7 + isLLA, // a7 gwxMark, // a8 mark, // a9 '\n', // diff --git a/test/runner/module-linux.ts b/test/runner/module-linux.ts index 137bfa3..da888c8 100644 --- a/test/runner/module-linux.ts +++ b/test/runner/module-linux.ts @@ -46,15 +46,12 @@ const wcscNative = (optionsPath: string, projectPath: string, outputPath: string }); }); }; -const wccNative = (optionsPath: string, projectPath: string, outputPath: string | undefined = undefined): Promise => { - if(!fs.existsSync(projectPath)){ - throw new Error('projectPath not exists.') - } +const wccNative = (optionsPath: string, outputPath: string | undefined = undefined): Promise> => { + const nodeExec = spawn( - path.resolve(__dirname, `../../../cache/nwjs-sdk-v${NW_VERSION}-linux-x64/nw`), - ['wcc.js', optionsPath], + path.resolve(__dirname, `../../cache/nwjs-sdk-v${NW_VERSION}-linux-x64/nw`), + [path.resolve(__dirname, './nwjs/compiler.js'), 'wcc', optionsPath], { - cwd: projectPath, env: { WX_DEBUG_COMPILER_OUTPUT: outputPath, }, @@ -77,8 +74,10 @@ const wccNative = (optionsPath: string, projectPath: string, outputPath: string outputPath && require('fs').writeFileSync(`${outputPath}/linux_err.js`, Buffer.concat(errData).toString()) if (0 === n) { let result = Buffer.concat(spwanData).toString(); - // process.stdout.write(result); - // result = JSON.parse(result); + result = result.split('---------------result------------------\n')[1] + process.stdout.write(result); + if (result[0] === '{') + result = JSON.parse(result); resolve(result); } else { process.stderr.write(Buffer.concat(errData).toString()); diff --git a/test/runner/module-windows.ts b/test/runner/module-windows.ts index ef3aa11..75985d4 100644 --- a/test/runner/module-windows.ts +++ b/test/runner/module-windows.ts @@ -1,12 +1,14 @@ import * as fs from 'fs' import { request } from "http"; import { CompilerOptions } from './types'; +import path from 'path' // 预先启动wine focker环境,再使用HTTP协议,最后销毁容器 const HTTP = { - POST: (type: 'wcc' | 'wcsc', compilerOptions: CompilerOptions): Promise => { + POST: (type: 'wcc' | 'wcsc', compilerOptions: CompilerOptions): Promise> => { return new Promise((resolve, reject) => { + compilerOptions.cwd = compilerOptions.cwd.replace(path.resolve(__dirname, '../../'), '/wrokspace') const postData = JSON.stringify(compilerOptions); const options = { @@ -22,17 +24,24 @@ const HTTP = { const req = request(options, (res) => { - console.log(`STATUS: ${res.statusCode}`); - console.log(`HEADERS: ${JSON.stringify(res.headers)}`); + // console.log(`STATUS: ${res.statusCode}`); + // console.log(`HEADERS: ${JSON.stringify(res.headers)}`); res.setEncoding('utf8'); let ret = "" res.on('data', (chunk) => { - console.log(`BODY: ${chunk}`); + // console.log(`BODY: ${chunk}`); ret += chunk }); res.on('end', () => { - console.log('No more data in response.'); - resolve(ret) + // console.log('No more data in response.'); + if (compilerOptions.lazyloadConfig) + { + resolve(JSON.parse(ret)) + } + else + { + resolve(ret) + } }); }); diff --git a/test/runner/nwjs/compiler.js b/test/runner/nwjs/compiler.js index 1cfa6af..baf7a1a 100644 --- a/test/runner/nwjs/compiler.js +++ b/test/runner/nwjs/compiler.js @@ -1,13 +1,15 @@ const fs = require("fs"); const compiler = require("./wcc"); -const args = process.argv.slice(1); +const args = process.argv.slice(2); const [type, p] = args; +// console.log('type:', type, '; options path:', p) const optionsData = fs.readFileSync(p).toString(); const options = JSON.parse(optionsData); (async () => { const result = await compiler[type](options); + console.log('---------------result------------------') if (typeof result === "string") { process.stdout.write(result); } else { diff --git a/test/spec/issue/102/issue102.spec.ts b/test/spec/issue/102/issue102.spec.ts index 8aa9602..5cec441 100644 --- a/test/spec/issue/102/issue102.spec.ts +++ b/test/spec/issue/102/issue102.spec.ts @@ -1,7 +1,7 @@ import assert from "assert" import path from "path" -import linux from '../../../runner/linux' -import windows from '../../../runner/windows' +import linux from '../../../runner/binary-linux' +import windows from '../../../runner/binary-windows' import * as fs from 'fs' describe("issue - 102", function () { diff --git a/test/spec/wcc/module/module.spec.ts b/test/spec/wcc/module/module.spec.ts index 8ea0b99..c2cd5e9 100644 --- a/test/spec/wcc/module/module.spec.ts +++ b/test/spec/wcc/module/module.spec.ts @@ -1,5 +1,6 @@ import assert from 'assert'; +import { describe } from "mocha"; import path from 'path'; import linux from '../../../runner/module-linux' import windows from '../../../runner/module-windows' @@ -8,8 +9,7 @@ import { execFileSync } from 'child_process'; describe("wcc - module", function () { this.beforeAll(() => { - // TODO: 启动docker wine容器 - execFileSync(path.resolve(__dirname, '../../../runner/nwjs/wine-prepare.sh'), { stdio: 'inherit' }) + // execFileSync(path.resolve(__dirname, '../../../runner/nwjs/wine-prepare.sh'), { stdio: 'inherit' }) }) describe("llw: linux output should deep equal with wine", function () { // afterEach(function(){ @@ -26,17 +26,37 @@ describe("wcc - module", function () { try { fs.mkdirSync(storagePath, { recursive: true }); } catch (error) {} - const n = JSON.parse(await linux.wcc(p, '')); - const w = JSON.parse(await windows.wcc(p)); - fs.writeFileSync( - `${storagePath}/wine-output.json`, - JSON.stringify(w, null, 4) - ); - fs.writeFileSync( - `${storagePath}/node-output.json`, - JSON.stringify(n, null, 4) - ); - assert.deepEqual(w, n); + + const w = await windows.wcc(p); + const n = await linux.wcc(p, ''); + console.log('windows:', typeof w) + console.log('linux:', typeof n) + + assert.equal(typeof n, typeof w); + if (typeof w == 'string') + { + fs.writeFileSync( + `${storagePath}/wine-output.json`, + w + ); + fs.writeFileSync( + `${storagePath}/node-output.json`, + n as string + ); + assert.equal(n, w); + } + else + { + fs.writeFileSync( + `${storagePath}/wine-output.json`, + JSON.stringify(w, null, 4) + ); + fs.writeFileSync( + `${storagePath}/node-output.json`, + JSON.stringify(n, null, 4) + ); + assert.deepEqual(n, w); + } }); }); });