问题
Update: It seems that, the problem is coming due to protobuf. I am fine with other solution as well, which help me to fix the Google protobuf issues. This problem boils down to:
- How to integrate Google protobuf with Typescript/Javascript for the browser?
I am retaining below question for the future purpose.
We have moved our application from Javascript to Typescript for obvious advantages of OOP etc..
Earlier invoking a direct javascript function from Html was as straight forward as:
<script>window.MyFunction()</script>
Now with Typescript, all the files are combined into a single autogenerated .js file.
In this single file, individual code of every file are isolated within System.register(). It typically looks something like:
System.register("<filename>", ["<import_1>", ..., "<import_N>"],
function (exports_13, context_13) {
"use strict";
...
function MyFunction () { ... } // somewhere inside the external function
}
In short, everything written within the .ts file is wrapped in an unnamed function after running the tsc compiler.
Now, I don't know how to invoke a function, which is trapped inside another function, which is in turn listed under System.register(...)
Question: What is the correct syntax to invoke such function externally from an Html file?
<script> ??? </script>
Update:
The HTML tries to invoke in following way in the body tag:
<script>
System.import("Main").then( // Main.ts is one of the file
function (module)
{
throw 0; // Temporary, to see if we reach till here
module.main(); // "main()" is the function, which is the entry point
});
</script>
In my code, I am using "browserify" to be able to use the Google protobuf for JS. The error comes for the protobuf related files only. Those definition and source files are present in .d.ts and .js formats.
The error is something like below:
js: Uncaught (in promise) Error: Fetch error: 404 NOT FOUND
Instantiating http://localhost:50000/folder/external/Server_pb
Loading http://localhost:50000/folder/external/_External
Loading Main
Note that, 50000 is a temporary port and the "folder" is just any folder where the .js are kept. The "Server_pb" is a custom protobuf file generated.
My problem can be aptly described quite similar as this link.
Related:
- What is mean by System.register in JS file?
- How to call a named module from a bundle (<-- can be helpful, but don't know the syntax as a newbie)
- How to start a Typescript app with SystemJS modules? (nearly duplicate, but unable to solve the problem with this approach yet)
- How do I get TypeScript to bundle a 3rd party lib from node_modules? (seems like another close match; trying to dig into this right now to fix the protobuf problem)
回答1:
With "google-protobuf" there are issues when used in the fashion of systemjs. It seems that Google has created it only for the nodejs. :-)
To be able to use the protobuf in Javascript for the browser, there are few things which we have to do manually. Such manual boilerplate work can be done using some scripts as well.
I am giving an iterative way, on how to achieve this:
The first step is to generate the protobuf for both JS and TS. Use following command for the same:
protoc <file1.proto> <file2.proto> ... <fileN.proto>--proto_path=<proto_folder> \--cpp_out=<cpp_folder> \--js_out=import_style=commonjs,binary:<js_folder> \--ts_out=import_style=commonjs,binary:<ts_folder>Note that, we are using the
commonjs(and notsystemjs). Legends:<proto_folder>= folder path where all thesefile1/2/N.protofiles are stored<cpp_folder>= folder path where you want the c++file1/2/N.pb.cc/hfiles to be stored<js_folder>= folder where you want thefile1/2/N_pb.jsfiles to be stored<ts_folder>= folder where you want thefile1/2/N_pb.d.tsfiles to be stored
Now in all the
.d.ts(Typescript definition) files, there are certain code lines, which will give compiler errors. We need to comment these lines. Doing manually, is very cumbersome. Hence you may usesed(orssedin Windows,gsedin Mac). For example, the lines starting with,sed -i "s/^ static extensions/\/\/ static extensions/g" *_pb.d.ts;- same as above for
static serializeBinaryToWriter - same as above for
static deserializeBinaryFromReader sed -i "s/google-protobuf/\.\/google-protobuf/g" *_pb.d.ts; // "./google-protobuf" is correct way to import
Now, while generating the
*_pb.d.ts, theprotoccompiler doesn't follow the packaging for Typescript. For example, if in yourfileN.proto, you have mentionedpackage ABC.XYZ, then thefileN.pb.hwill be wrapped innamespace ABC { namespace XYZ { ... } }. The same doesn't happen in the case of Typescript. So we have to manually add these in the file. However, here it won't be a simple find/replace as above. Rather, we have to find only the first occurance of anyexport class(which is of generated proto) and wrap the namespaces. So below is the command:sed -i "0,/export class/{s/export class/export namespace ABC { export namespace XYZ {\\n &/}" fileN_pb.d.ts;sed -i -e "\$a} }" fileN_pb.d.ts;
Initial importing of the
google-protobufpackage has to be prefixed with./in the case of generated_pb.jsfile as wellsed -i "s/google-protobuf/\.\/google-protobuf/g" *_pb.js;
Now compile the the custom Typescript files with
tsc -p "<path to the tsconfig.json>", where thetsconfig.jsonmay look like (see arrow):{ "compileOnSave": true, "compilerOptions": { "removeComments": true, "preserveConstEnums": true, "module": "CommonJS", <======= "outDir": "<path to generated js folder>", }, "include": ["../*"], "files": ["<path to file1.ts>", ..., "<path to file2.ts>" }Now a very important step. All the references to the the generated
*_pb.d.tsfiles, should be referred in 1 of your custom file. That custom file may contain the wrappers around the generated classes if it's required. This will help in limiting string replacement only in that file, which is explained in the upcoming step. For example, create a custom file name asMyProtobuf.tsandimportyourprotoas following:import * as proto from './fileN; // from fileN.d.ts
In above step, it's important to note that the name
"proto"is crucial. With that name, the.jsfiles are auto generated. If there are several proto files in your project, then you may have to create yet 1 more file which exports all of them and then import that 1 file:// in 'MyProtobufExports.ts' fileexport * from './file1'export * from './file2'export * from './fileN'import * as proto from './MyprotobufExports // in MyProtobuf.ts file
With above 2 steps, the usage of the protobuf as,
var myClass = new proto.ABC.XYZ.MyClass;Now the continuation of the important step we discussed above. When we generate the equivalent
_pb.jsand our custom.jsfiles, still the special name-symbolprotowill not be found somehow. Even though everything is wrapped. This is because the autogenerated JS files (from TS files), will declare avar proto. If we comment that then, that issue is gone.sed -i "s/var proto = require/\/\/ &/g" Protobuf.js;
The final step is to put the
browserifyon all the.jsfiles into a single file, as below. Due to this, there will be only single.jsfile, we have to deal with [good or bad]. In this command, the ordering is very important.file1_pb.jsshould come beforefile2_pb.js, iffile1.protois imported byfile2.protoor vice a versa. If there is noimportthen the order doesn't matter. In any case the_pb.jsshould come before the custom.jsfiles.browserify --standalone file1_pb.js fileN_pb.js MyProtobuf.js myfile1.js myfileN.js -o=autogen.js
Since the code is browserified, the calling of function can be done in following way:
window.main = function (...) { ... } // entry point somewhere in the fileN.ts file<script>main(...)</script> // in the index.html
With the above steps only, I am able to make the "google-protobuf" work within my project for the browser.
来源:https://stackoverflow.com/questions/51096798/how-to-invoke-a-javascript-function-generated-from-typescript-trapped-within