How to exclusively compile a custom source for OpenLayers 3 - javascript

I have created a new Source for OpenLayers 3 that works similarly to ol.source.ImageWMS but with a modified request structure since the remote source is not a WMS.
The new source work as expected when used together with ol-debug.js or if compiled together with the OpenLayers library as a custom build.
But what I want is so exclusively compile my extension and then include it in my project as an extension to OpenLayers.
For example
<script src="ol.js"></script>
<script src="imageSpecialSource.js"></script>
I have tried to exclude the some of the library symbol names / patterns under the export section in the json config file. But including the compiled js file will make OpenLayers to throw an error.
Uncaught TypeError: a.cf is not a function
So, is it possible to compile a custom extension exclusively that can then be included as a separate file?

First of all, know that there's a more up-to-date tutorial that explains how to compile OpenLayers 3 along with your own library. See here: http://openlayers.org/en/v3.14.1/doc/tutorials/closure.html
The compilation process requires a config.json file. Here's a snippet of the config file from the above tutorial:
{
"lib": [
"node_modules/openlayers/src/**/*.js",
"node_modules/openlayers/build/ol.ext/**/*.js",
"src/**/*.js"
],
"compile": {
"closure_entry_point": "app",
"externs": [
"node_modules/openlayers/externs/bingmaps.js",
"node_modules/openlayers/externs/closure-compiler.js",
"node_modules/openlayers/externs/geojson.js",
"node_modules/openlayers/externs/proj4js.js",
"node_modules/openlayers/externs/tilejson.js",
"node_modules/openlayers/externs/topojson.js"
],
...
See that in it, what's inside lib gets compiled together, that's OpenLayers and your own custom code.
Then, look at the externs section. These defined externs files that allow the code to be used within ol3 without the Closure Compiler complaining about classes and methods it would otherwise not know about.
In order to compile your code without ol3, you could remove it from the lib section and add the ol3 extern file in externs instead. In ol3, that file is located in build/ol-externs.js. If it's not there, you can generate it by running:
node tasks/generate-externs.js build/ol-externs.js
You can take a look at the OL3-Google-Maps library that does exactly that. Its dist/ol3gm.js file doesn't contain OpenLayers: https://github.com/mapgears/ol3-google-maps/
Look at the json config file, which is named ol3gm.json:
https://github.com/mapgears/ol3-google-maps/blob/master/build/ol3gm.json. You'll see that the ol-externs.js file is used in there.

Related

TypeScript: how to "import" js and other ts ressources/modules without have them merged in the emitted js file?

I read the Typescript Handbook and searched quite a bit but I still cannot find the proper way to do that: use existing js files as reference in my ts files and not have them in the emitted js file.
For instance, let's say my structure is like this in my web project:
- js/
- old-lib.js
- app-output.js
- new-lib-output.js
- app-src/
- newLib/
- my-lib.ts
- tsconfig.json
- app/
- my-app.ts
- tsconfig.json
In my-app.ts file, I have something like this:
namespace App
{
export class MyApp
{
... some logic which requires existing code from my old-lib.js file.
}
}
So if I use a triple-slash reference to the said js file, my output file is gonna include the source lib file as well. How do I get rid of that? I understand I can create and Interface to map declare the types and functions contained in my old js but is there a better way?
Also, following the same principle, I want to use a new lib (with different functions of course) and have it compiled separately (new-lib-output.js and app-output.js). Everywhere I look I see examples of modules importation but nobody talks about the exported js as in all examples it seems the output is all together in the same file.
My goal is to be able to reuse js files in different contexts, now always with my app but with other apps.
Thanks for any help you could provide!!

Load transpiled TS2 with HTML <script> tag?

I'm really new to Typescript2. I'm loving it and have written a module that I can easily import and use in other Typescript2 projects. But I also want my library to be able to be used as a standalone by simply using an HTML tag. When I do that, though, the browser complains "exports is undefined."
I'm guessing I have to write some sort of javascript that imports my module and instantiates it, and then have my browser load that wrapper script. Am I on the right track? How do I create a typescript that transpiles into something that I can just load natively in the browser with a script tag that just loads and instantiates?
You are on the right track. What you need is a module loader. For the browser, requirejs is mentioned in the typescript documentation, although there are several options (requirejs, browserify, webpack, rollup).
I use requirejs with typescript, so I have experience doing the following steps:
For requirejs, you could do the following to get started.
Ensure that you are compiling to modules in the AMD format when running the typescript compiler command (tsc) by using the typescript configuration file or command-line compiler option arguments
// tsconfig.json
{
"compilerOptions": {
"module": "amd"
}
}
Get requirejs
One source is http://requirejs.org/docs/download.html
Note the name of the .js file that is output by the typescript compiler
For example "my-script.js"
Add a script tag to the page
For example <script data-main="scripts/my-script" src="require.js"></script>
There is a simple "getting started" page for requirejs http://requirejs.org/docs/start.html
As a bonus, to export multiple modules to a single file, you can use the --outFile compiler option for typescript.

Make pdf.js 1.5 and require.js play nice together

In my project I have long used require.js together with the pdf.js library. Pdf.js have until recently been putting itself on the global object. I could still use it in my requirejs config by using a shim. The pdfjs library will in turn load another library called pdf.worker. In order to find this module the solution was to add a property to the global PDFJS object called workerSrc and point to the file on disk. This could be done before or after loading the pdfjs library.
The pdfjs library uses the pdf.worker to start a WebWorker and to do so it needs the path to a source file.
When I tried to update the pdfjs library in my project to a new version (1.5.314) the way to load and include the library have changed to use UMD modules and now everything get's a bit tricky.
The pdfjs library checks if the environment is using requirejs and so it defines itself as a module named "pdfjs-dist/build/pdf". When this module loads it checks for a module named "pdfjs-dist/build/pdf.worker". Since I have another folder structure I have added them to my requirejs config object with a new path:
paths: {
"pdfjs-dist/build/pdf": "vendor/pdfjs/build/pdf",
"pdfjs-dist/build/pdf.worker": "vendor/pdfjs/build/pdf.worker"
}
This is to make the module loader to find the modules at all. In development this works great. When I try to use the requirejs optimizer in my grunt build step however, it will put all of my project files into one single file. This step will try to include the pdf.worker module as well and this generates an error:
Error: Cannot uglify2 file: vendor/pdfjs/build/pdf.worker.js. Skipping
it. Error is: RangeError: Maximum call stack size exceeded
Since the worker source needs to be in a single file on disk I don't want this module to be included.
So I've tried two different config-settings in the requirejs config.
The first attempt was to override the paths property in my grunt build options:
paths: {
"pdfjs-dist/build/pdf.worker": "empty:"
}
The second thing to test is to exclude it from my module:
modules: [{
name: "core/app",
exclude: [
"pdfjs-dist/build/pdf.worker"
]
}]
Both techniques should tell the optimizer not to include the module but both attempts ended up with the same error as before. The requirejs optimizer still tries to include the module into the build and the attempt to uglify it ends up with a RangeError.
One could argue that since the uglify step fails it will not be included and I can go about my bussiness, but if the uglify step should happen to start working at a new update of pdfjs - what then?
Can anyone help me figure out why the requirejs config won't just exclude it in the build step and how to make it do so.
I found out what the core of my problem was and now I have a way to solve the problem and make my build process to work. My build step in grunt is using grunt-contrib-requirejs and I needed to override some options in the config for this job.
I didn't want the pdf.worker module to be included in my concatenated and minified production code.
I didn't want r.js to minify it only to later exclude it from the concatenated file.
I tried to solve the first problem thinking that it would mean that the second problem also should be solved. When I figured out the two were separate I finally found a solution.
In the r.js example on github there is a property named fileExclusionRegExp. This is what I now use to tell r.js not to copy the file over to the build folder.
fileExclusionRegExp: /pdf.worker.js/
Second, I need to tell the optimizer to not include this module in the concatenated file. This is done by overriding the paths property for this module to the value of "empty:".
paths: {
"pdfjs-dist/build/pdf.worker": "empty:"
}
Now my grunt build step will work without errors and all is well.
Thanks to async5 for informing me about the bug with uglify and the pdf.worker. The workaround is applied in another grunt task that uglify the worker and copies it into the build-folder separately. The options object for the grunt-contrib-uglify task will need this property in order to not break the pdf.worker file:
compress: {
sequences: false
}
Now my project works great when built for production.

What's the best way to concatenate vendor js files?

In my Angular JS app, I'm using a lot of third party packages, mainly maintained via Bower.
When I use Grunt to concatenate all of them into one mega file, I'm getting errors when I load my page, for example that
Uncaught ReferenceError: angular is not defined and
GET http://localhost:8080/theproj/v4/dist/app/bootstrap.css.map 404 (Not Found)
What is the best way to properly concatenate all these files to ensure that everything loads in the right order and doesn't cause problems?
First issue: A lot of times third party libraries must be loaded in a particular order. That looks like like it's the source of your first issue. Something is trying to use angular and it's getting loaded before the angular code. You should refactor your grunt task to use a pre-defined order for third party libraries.
Second issue: You probably are missing the .map file. This is a file used by Chrome dev tools to show you the original source for the css (sass or less). Either provide the map file, or delete the reference to it from bootstrap.css. Or just ignore the error, it's only an error when you have chrome dev tools open, and doesn't actually affect your application.
For the issue of the correct order for your javascript files, i had that problem in a larger project where noone really had a clue which was the correct order.
Luckily we found that the Google Closure Compiler does exactly this: https://github.com/google/closure-compiler
You give it all your js files and it analyzes them all and concatenates them in order
$ java -jar compiler.jar --js_output_file=out.js in1.js in2.js in3.js ...
There is even a grunt plugin for the connection: https://github.com/gmarty/grunt-closure-compiler
'closure-compiler': {
frontend: {
closurePath: '/src/to/closure-compiler',
js: 'static/src/frontend.js',
jsOutputFile: 'static/js/frontend.min.js',
maxBuffer: 500,
options: {
compilation_level: 'ADVANCED_OPTIMIZATIONS',
language_in: 'ECMASCRIPT5_STRICT'
}
}
},
Another way would be to change your javascripts into AMD or CommonJS modules, this way you don't have to worry about the correct order. RequireJS (http://requirejs.org/docs/start.html) is a possibility for AMD for example or Webpack (http://webpack.github.io/) ...or many many others.

How to load only module related content in SPA?

I'm working on a large backbone single page application where we've all the JS files specified in order just before </body> tag. Then, for production, using grunt-usemin we concatenate and make single minified app.min.js file for production. Also all the templates are converted to JS using grunt-contrib-handlebars and concatenated into the same app.min.js file.
As the code is growing, size of app.min.js file has gone up to 1.25MB and still this application has long way to go. There are many major sections yet to be developed.
At the same time, I don't want to load 5 or more JS files and same number of templates when user visits to each screen. Rather, I wanted to have modules and load single module file and user can browse through whole of the module without loading any other JS file.
Also I want those module files to be minified and optimized and revved (for cache busting) for production
I was looking through require.js and it's optimizer. I've just started implementing one of our next module using require.js and when I came to building for deployment, it seems that r.js optimizer creates single output file which brings me back to square.
Is it not possible to do what I'm looking for using require.js, or am I missing something? Or is there any better solution of my problem than require.js?
NOTE: Though there is a benefit of using loader even though single big file is loaded at once, I don't want to load whole code when I just need one module of code.
you can build into multiple files.
inside requirejs:compile:options: do the below
modules: [
{
name: '../build1',
include: [
// 3rd party libs
'backbone', //the reference to these files are present in paths: property
'underscore'
]
},
//build 2
{
name: '../build2',
include: ['build2ReqFile1','build2ReqFile2'],
// Excludes all nested dependencies and built dependencies from "common"
exclude: ['../build1','build2UnReqFile1']
},
//build 3
{
name: '../build3',
include: ['build3ReqFile1','build3ReqFile2'],
// Excludes all nested dependencies and built dependencies from "common"
exclude: ['../build1','../build2','build3UnReqFile1']
},
]
Updating with explanation
yep this is for single page application.
Few things to understand here - whenever you define i.e. define('app/test',['app/load1','app/load2'],function(load1Ref, load2Ref){}), new script tag is added to dom and a data-xyz = name of module i.e. <script data-xyz = 'app/test' src = '' type='text/javascript'> is added. I dont remember what the name of xyz is. The returned value of the define is stored in the context data-xyz. So when ever you do require(['app/test']) it returns the value present in data-xyz of app/test script.
Now when you build, all the require and defines that are referenced are built into one single .js. SO by doing the above you can have multiple builds where in you mention which of the defines each of these individual builds should include or exclude.
So these builds are nothing all the javascript files minified and compiled into one/multiple files. Depending on which files you require you need to include these built files.
If you require 'build2ReqFile1','build2ReqFile2' then you can just include build2.js in your code, if you want backbone and underscore along with 'build2ReqFile1','build2ReqFile2' then you need to include build2.js and build1.js
Is it clear?

Categories