All posts by Vjeko

In the last post, I explained how to use tasks, but I didn’t explain yet how to make them useful. Tasks are most useful when they automate certain file operations, and gulp helps with reading and writing files. However, if you need to perform a transformation, then you need to look for plugins that can get that work done for you.

In this post you’ll learn how to set up and use various plugins that can automate some common file operations.

As gulp documentation says: Gulp plugins are Node Transform Streams that encapsulate common behavior to transform files in a pipeline – often placed between src() and dest() using the .pipe() method.

Let’s imagine that:

You are creating a control add-in and you are writing a lot of JavaScript

You want to isolate your JavaScript features into individual files, one file per feature, and that you want to combine these files all into one file that will be included in your control add-in

You want to write modern JavaScript, but still want to make sure all browsers will be able to run it (remember, anybody using earlier versions of Windows than Windows 10, and using the NAV/BC universal client, will be running Internet Explorer in there)

You want to minify the resulting file to consume space

You want to zip the contents of your control add-in into the Resource.zip file

All valid assumptions, correct? If you are anything like me, this is what you do every day.

Now, some of these things can be done manually, some not really, but all of them certainly can be automated using gulp. Our job would need to contain the following transformations:

Bundle JavaScript files into a single file

Transpile that file into a lower version of JavaScript

Minify that file

Package the contents of the resource directory to produce the Resource.zip file

The more astute of you will immediately notice that all of this cannot be one task. Unless you want to complicate your life, one task should only perform those operations that can be handled with a single pipeline. A single pipeline is what starts with a stream of input files and transforms them until you cannot transform it. This means that as long as the output of the previous operation can be the input into the next operation, you can pipe that output down the pipeline. Packaging the contents of the resource directory is an operation that cannot use the output of the previous operation, so you need to isolate it into a separate task.

Another reason why you may want to separate operations into individual tasks is when an operation makes sense on its own. If you can imagine any of the previous operations as something you’d ever want to run individually, then that operation should also be isolated into a separate task. Tasks can be combined in different ways, and I’ll address that as well in one of the future posts, so don’t be afraid to split up operations that can benefit from splitting.

In my example, packaging the Resource.zip file is an operation that can be done independently of everything else. JavaScript is not the only content of the resource file: you may change CSS, you may add or modify images, configure the manifest, you name it. In all these situations, you may want to package the resource file independent of the JavaScript operation. It just makes sense to turn it into a task of its own.

Good, so we have two tasks then, with the following flow:

Preparing JavaScript file

Collect all JavaScript files into a single stream

Pipe that into a function that combines all the files into one

Pipe that into a function that transpiles your code to a lower version

Pipe that into a function that minifies your code

Store the result into a destination inside the resource subdirectory

Zip the content of your resource subdirectory

Collect all files underneath the resource subdirectory into a single stream

Pipe that into a function that zips the content of the stream

Store the result into a destination directory

Good. Now that we know what steps we need to do, let’s create the two tasks with minimum operations that gulp can do on its own:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

constgulp=require("gulp");

functionjavaScript(){

returngulp

.src("src/*.js")

.pipe(gulp.dest("resource/Script"));

}

functionresourceZip(){

returngulp

.src("resource/**")

.pipe(gulp.dest("build"));

}

So far, so good.

Let’s focus now on JavaScript task first. The first operation we need to do on the files is to bundle (or concatenate) them. If bundling the files was the only operation you need, then you wouldn’t really need a plugin as Node.js contains all the necessary APIs for that. However, if your bunding is just a pass-through operation in a pipeline, you’ll need a plugin. Let’s look for a plugin.

Normally, you’d do a general google search, but gulp maintains its own plugin directory which makes your life much easier. Go to https://gulpjs.com/plugins/ and search for “concatenate”, you’ll find a number of plugins. For my example, I chose gulp-concat. When I need a plugin, I look in its documentation to see whether it can do everything I need, and how simple it is to use. I also look at its github repository (all of plugins I used have a github repository) where I can see how much “alive” it is: how many commits and how often, are there pull requests merged into it, how many forks are out there, is the author responding to issues, etc. All of it can contribute to a perceived reliability rating that finally makes me choose one plugin over another.

Good. Now that we know that gulp-concat plugin can do the job for you, how do you use it?

If you remember the first post in the series, you can remember how you imported gulp itself into your workspace: by using npm. Gulp plugins are typically package, and to import them into your workspace, you’ll also use npm. Just remember, any gulp plugin packages that you import are dependencies in your development environment, therefore you must use the –save-dev option. Let’s import gulp-concat:

JavaScript

1

2

npmigulp-concat--save-dev

Now that gulp-concat is a part of your solution and under the management of npm, you can use it inside your gulpfile.js script:

JavaScript

1

2

constconcat=require("gulp-concat");

Now, we can use the concat in the pipeline like this:

JavaScript

1

2

.pipe(concat("controlAddIn.js"))

The next step is to transpile that using babel. Again, a little search should help you find the gulp-babel plugin that you can then first install into your workspace. However, if you simply use this, you may realize that it does not work.

JavaScript

1

2

npmigulp-babel--save-dev

Some plugins use other packages that they don’t bundle directly, but allow you to choose specific versions of those packages, and sometimes they will provide different install scripts. To install gulp-babel with the necessary dependencies, you should use this:

JavaScript

1

npmi--save-dev gulp-babel@babel/core@babel/preset-env

… then declare inside the gulpfile.js script:

JavaScript

1

2

constbabel=require("gulp-babel");

… and finally pipe the concatenated file into it:

JavaScript

1

2

.pipe(babel({presets:["@babel/env"]}))

Good job! Finally, we are ready for the last step of the first task – the minification. A quick search should reveal any number of possibilities, but I’ll go with this one:

JavaScript

1

2

npmigulp-uglify--save-dev

When it’s installed, declare it:

JavaScript

1

2

constuglify=require("gulp-uglify");

… and then pipe the transpilation results into it:

JavaScript

1

2

.pipe(uglify())

And you are finished. If you did everything correctly, this is now your first task:

JavaScript

1

2

3

4

5

6

7

8

9

functionjavaScript(){

returngulp

.src("src/*.js")

.pipe(concat("controlAddIn.js"))

.pipe(babel({presets:["@babel/env"]}))

.pipe(uglify())

.pipe(gulp.dest("resource/Script"));

}

For the second task we need to locate a plugin that can zip a stream of files. Again, search the gulp plugin catalog, and you should discover gulp-zip. You are an expert by now, so you know that you first need to install it:

JavaScript

1

2

npmigulp-zip--save-dev

… then declare it:

JavaScript

1

2

constzip=require("gulp-zip");

… and finally use it in your zip pipeline:

JavaScript

1

2

.pipe(zip("Resource.zip"))

If you did everything correctly, this is the second task:

JavaScript

1

2

3

4

5

6

7

functionresourceZip(){

returngulp

.src("resource/**")

.pipe(zip("Resource.zip"))

.pipe(gulp.dest("build"));

}

Perfect. The only thing that’s missing is exporting the tasks from your gulpfile.js:

JavaScript

1

2

3

module.exports.javaScript=javaScript;

module.exports.resourceZip=resourceZip;

You can try to see if these two tasks now work:

JavaScript

1

2

gulp javaScript

Take a look inside your resource/Script directory and you should fine the controlAddIn.js script in there.

Then, run this:

JavaScript

1

2

gulp resourceZip

Now take a look inside the build directory and you should fine the Resource.zip file in there.

Cool, but now we need another task that first invokes the javaScript task and then the resourceZip task in sequence. Luckily, gulp helps with that, too. The gulp.series() method creates a task that runs specified tasks in a serial sequence, one after another. Just export one more task from your gulpfile.js:

JavaScript

1

2

module.exports.build=gulp.series(javascript,resourceZip);

If you now delete the files that previous two tests created, and then run this:

JavaScript

1

2

gulp build

… you will see that it has correctly built your Resource.zip file.

Just in case, here’s my latest state of the gulpfile.js:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

constgulp=require("gulp");

constconcat=require("gulp-concat");

constbabel=require("gulp-babel");

constuglify=require("gulp-uglify");

constzip=require("gulp-zip");

functionjavaScript(){

returngulp

.src("src/*.js")

.pipe(concat("controlAddIn.js"))

.pipe(babel({presets:["@babel/env"]}))

.pipe(uglify())

.pipe(gulp.dest("resource/Script"));

}

functionresourceZip(){

returngulp

.src("resource/**")

.pipe(zip("Resource.zip"))

.pipe(gulp.dest("build"));

}

module.exports.javaScript=javaScript;

module.exports.resourceZip=resourceZip;

module.exports.build=gulp.series(javaScript,resourceZip);

In my next post, I’ll cover two more topics: deploying the control add-in to an NAV / Business Central instance using PowerShell from a gulp task, and passing configuration to gulp tasks from JSON configuration files.

Gulp – being a task automation tool – is all about tasks. Anything you do about programming, which is not writing the code itself, can be considered a task, and can very likely be automated. Some tasks are automated already, like compiling your code, building your app. In case of AL development, there are many more already automated tasks, like creating a demo workspace or deploying your app to NAV. If you can automate them, tasks are immensely powerful because they save time and eliminate error.

If you are a web developer, your tasks can include bundling your source files and minifying them. For me, when building pre-2018 control add-ins, most important tasks were bundling source files, zipping the resource file, deploying a control add-in and importing the control add-in using PowerShell. All of these I have automated using gulp, and I’ll explain every single one of them in this series.

In my last post I covered the very basics of getting started with gulp in VS Code. It was all about getting gulp up and running under the hood of VS Code, and about writing your very first gulp task. It didn’t do much, but it showed you what gulpfile.js is good for and how to export tasks from it, that you can later use from VS Code.

However, I mentioned that there is much more to exporting tasks than it seems on the surface, and I promised to blog more about it. So here we are. Tasks.

Strange times. I never stop to be amazed with the kinds tools that invaded our pristine development environments. One of those tools is gulp, and if you haven’t heard of it, don’t worry. Why should you hear of it?

A couple of weeks ago I introduced my talk on gulp by explaining how I made it simplify and speed up my development of control add-ins. If you are a real NAV developer, and if you didn’t classify this entire series on gulp into the “who cares” category, then chances are you’d stick with me today and learn how to set up gulp and start using it in VS Code.

In this post, I’ll explain what (and why) you need to do to get gulp up and running, and then I’ll create a very simple gulp task. This is the first post in a series where I hope to build an end-to-end example that you can even put to practical use in your projects.

Nearly two and a half years ago I had a dream. It was about codeunit references and codeunit interfaces. And today, nearly two and a half years later I am still here, still having the same dream. We still cannot do codeunit interfaces in AL. But it doesn’t mean we don’t need them. We do. We badly do.

Two things triggered me to write this post about how badly we need codeunit interfaces, or any kind of polymorphism: a NAV TechDays talk, and a github project I saw.

Posts navigation

When comparing .NET variables, including Enums, you cannot use C/AL comparison operators. To compare .NET variables, you must use the Equals method (of the System.Object type) that all .NET types implement or inherit. So, instead of IF var1 = var2, or IF var1 = var1.EnumValue (in case of an Enum), just write IF var1.Equals(var2), or IF var1.Equals(var1.EnumValue).

I see this mistake often being made or attempted by developers, even though it has been documented inside .NET Interoperability documentation since it was introduced with 2009 R2.

Make sure that you don’t access the Microsoft.Dynamics.NAV JavaScript object before the document ready event fires. If you do so, you might experience problems on Chrome when the user refreshes the browser (F5). It appears that on refresh Chrome loads (and runs) scripts in different order, and depending on how complex scripts included in your project are, your code might get executed before Microsoft’s script is loaded, and it will cause nasty script errors. This occurs only on Chrome on PC.

When you have to format C/AL variables (numbers, dates/times, booleans) for exchange with other apps, call FORMAT(variable,0,9) instead of simply FORMAT(variable). The format 9 formats the variable according to XML standards, and this value can then be interpreted correctly on any system with any regional settings. This is useful also when passing string-formatted values from C/AL to C# or JavaScript.

To check if a BLOB field has a value, you call its HASVALUE function. For example: IF Item.Picture.HASVALUE THEN;

In older versions, earlier than NAV 2009, you had to call CALCFIELDS before you could check HASVALUE, which – if you think of it, did not make much sense. This was changed in NAV 2009, so ever since that version you can check HASVALUE before you decide to call CALCFIELDS first. It makes all the sense – you don’t need to pull up to 2GB of data over just to see if anything is inside.

If you are an old-school guy (or just old, as me), and you CALCFIELDS first, HASVALUE next, maybe it’s time for you to reconsider it.

Rembember – the pattern is: IF Field.HASVALUE THEN Rec.CALCFIELDS(Field);