This month I learned - December 2017

8 min read

It's shortly before the new year's party with my friends, and I thought I could finish this year with a last "This month I learned post".

I have to say it was a crazy, intense and enjoyable year! I wrote 48 TIL posts spoke at 21 events, visited 13 countries including the United States, Australia, and New Zealand and most importantly I met an uncountable number of wonderful people!

I want to say a big THANK YOU to all the lovely people that I had good conversations with, that listened to me while I was struggling in the new field of developer relations and that made me always feel welcome! ❤️❤️❤️

But what did I learn in December you may ask. Here we go. :D

BTW I'm considering to send these monthly learnings also via a newsletter. If you'd be interested in that please let me know.

  • How to figure out the Node.js entry script with process.mainModule or require.main

    I was reading the import.meta proposal for JavaScript over the weekend. This proposal aims to solve e.g. the issue of accessing module meta information like what the script current element is.

    // in Frontend land
    // index.html
    <script src="foo.js"></script>
    
    // foo.js
    const currentScript = document.currentScript

    This is how you could do it in the browser but how does this work in Node.js? This brings to me learning of the weekend. 🎉

    Let's do a quick refresher first: in Node.js every module and required file is wrapped in a so called module wrapper.

    (function(exports, require, module, __filename, __dirname) {
      // Module code actually lives in here
    });

    This is were the require function and convenience objects like __filename and __dirname are coming from. In Node.js the is no currentScript but rather you have one entry script which then requires probably thousands of other modules. How could you now figure out if a script is the entry script?

    It turns out there are two ways to do this. There is require.main and process.mainModule. So let's have a look what is defined in these two.

    // test.js
    console.log(require.main);
    console.log(process.mainModule);
    
    // -------------------------------------
    // output of `$ node test.js`
    Module {
      id: '.',
      exports: {},
      parent: null,
      filename: '/private/tmp/foo.js',
      loaded: false,
      children: [],
      paths:
       [ '/private/tmp/node_modules',
         '/private/node_modules',
         '/node_modules' ] }
    Module {
      id: '.',
      exports: {},
      parent: null,
      filename: '/private/tmp/foo.js',
      loaded: false,
      children: [],
      paths:
       [ '/private/tmp/node_modules',
         '/private/node_modules',
         '/node_modules' ] }

    Okay... so you can get the filepath of the entry module by accessing require.main.filename or process.mainModule.filename and these two objects also include way more useful information.

    To figure out if a module is the entry script you can then check against the module object.

      const isEntryScript = require.main === module;
      const isAlsoEntryScript = process.mainModule === module;

    But are require.main and process.mainModule actually the same thing?

    // test.js
    console.log(require.main === process.mainModule);
    
    // -------------------------------------
    // output of `$ node test.js`
    true

    Huh, that's interesting – they are... So what's the difference then? The docs are relatively fuzzy on that.

    The difference is that if the main module changes at runtime, require.main may still refer to the original main module in modules that were required before the change occurred. Generally, it's safe to assume that the two refer to the same module.

    So what does that mean? I decided to dig the Node.js core code a little bit.

    process.mainModule is defined in node/lib/modules.js:

    Module._load = function(request, parent, isMain) {
      // ...
    
      if (isMain) {
        process.mainModule = module;
        module.id = '.';
      }
    
      Module._cache[filename] = module;
    
      tryModuleLoad(module, filename);
    
      return module.exports;
    };

    require.main is defined in node/lib/internals/modules.js:

    function makeRequireFunction(mod) {
      // ...
      require.main = process.mainModule;
      // ...
      return require;
    }

    I didn't dig the internals any further but the require function that we all use every day holds an actual reference to process.mainModule. This is why they are really the same thing. What happens now if we change process.mainModule or require.main?

    // test.js
    const bar = require('./foo');
    console.log(process.mainModule);
    console.log(require.main);
    
    // foo.js
    // changing both values
    process.mainModule = 'schnitzel';
    require.main = 'pommes';
    
    // -------------------------------------
    // output of `$ node test.js`
    schnitzel
    Module {
      id: '.',
      exports: {},
      parent: null,
      filename: '/private/tmp/foo.js',
      loaded: false,
      children:
       [ Module {
           id: '/private/tmp/bar.js',
           exports: {},
           parent: [Circular],
           filename: '/private/tmp/bar.js',
           loaded: true,
           children: [],
           paths: [Array] } ],
      paths:
       [ '/private/tmp/node_modules',
         '/private/node_modules',
         '/node_modules' ] }

    Aha! It turns out that if we set process.mainModule to something else during runtime (I have no idea why I would do this, but yeah ¯_(ツ)_/¯) require.main still holds the reference to the initial main module then.

    Edited:

    Alexandre Morgaut pointed out that require.main is part of the CommonJS Spec and that's why it's in Node.js core.

  • The global `Reflect` object, its use cases and things to watch out for

    I was reading the source code of one of Sindre Sorhus' modules today. It was the module on-change which provides the functionality of watching changes performed on Objects or Arrays. The module doesn't include much code – as it's very often the case for Sindre's modules. They serve a single purpose and usually are quick to use high-quality utilities.

    The whole module is 23 lines of code.

    'use strict';
    
    module.exports = (object, onChange) => {
        const handler = {
            get(target, property, receiver) {
                try {
                    return new Proxy(target[property], handler);
                } catch (err) {
                    return Reflect.get(target, property, receiver);
                }
            },
            defineProperty(target, property, descriptor) {
                onChange();
                return Reflect.defineProperty(target, property, descriptor);
            },
            deleteProperty(target, property) {
                onChange();
                return Reflect.deleteProperty(target, property);
            }
        };
    
        return new Proxy(object, handler);
    };

    I expected the usage of Proxy in this module, but there are things in this code that have been new to me. These are the reason for me writing this post today. 🎉

    First of all, the code uses a global Reflect object which I haven't seen before. I headed to MDN to look at the definition.

    Reflect is a built-in object that provides methods for interceptable JavaScript operations. These methods are the same as those of proxy handlers. Reflect is not a function object, so it's not constructible.

    For me the definition was not really helpful at this point because I was looking for an answer to the question "Why should I use it?".

    Side note: should the information why we have a global Reflect object be on MDN? Neither the MDN entry nor the EcmaScript spec paragraph answer that question.

    After digging and googling a while, I came again across the fact that Reflect includes the same methods as the defined traps being available in a proxy in a StackOverflow thread.

    These methods are:

    • apply()
    • construct()
    • defineProperty()
    • deleteProperty()
    • get()
    • getOwnPropertyDescriptor()
    • getPrototypeOf()
    • has()
    • isExtensible()
    • ownKeys()
    • preventExtensions()
    • set()
    • setPrototypeOf()

    The primary use case of the Reflect object is it to make it easy to interfere functionality of an existing object with a proxy and still provide the default behavior. You can always just use the defined counterpart to the Proxy handler in the Reflect object and can be sure that the default behavior stays the same.

    const loggedObj = new Proxy(obj, {
      construct: function(target, argumentsList) {
        // additional functionality
        // ...
        return Reflect.construct(target, argumentsList);
        // ☝️ same as `return new target(...argumentsList);`    
      },
      get: function(target, name) {
        // additional functionality
        // ...
        return Reflect.get(target, name);
        // ☝️ same as `return target[name];`
      },
      deleteProperty: function(target, name) {
        // additional functionality
        // ...
        return Reflect.deleteProperty(target, name);
        // ☝️ same as `return delete target[name];`
      }
    });

    These methods are very convenient because you don't have to think of syntactic differences in JavaScrict for specific operations and can just use the same method defined in Reflect when dealing with proxies.

    But there is more...

    You might have noticed that some methods defined in the Proxy object have the same name as functions defined in the Object prototype. These look the same but can behave slightly differently. So you have to watch out there.

    defineProperty is a good example. It behaves differently in case a property can't be defined on an object.

    // setup
    const obj = {};
    Object.defineProperty(obj, 'foo', {configurable: false, value: 42});
    
    // differences
    Object.defineProperty(obj, 'foo', {value: 43});
    // ☝️ this throws `can't redefine non-configurable property "foo"`
    
    Reflect.defineProperty(obj, 'foo', {value: 43});
    // ☝️ this returns `false`

    With this knowledge let's have another look at Sindre's module again, now with added comments.

    'use strict';
    
    module.exports = (object, onChange) => {
      const handler = {
        get(target, property, receiver) {
          try {
            // this goes recursively through the object and 
            // creates new Proxies for every object defined
            // in the target object when it is accessed
            // 
            // e.g. `a.b.c = true` triggers: 
            // - `get` for accessing `b`
            // - `defineProperty` for setting `c`
            return new Proxy(target[property], handler);
          } catch (err) {
            // ☝️ throws when a new Proxy is iniatlized with a string or a number
            // which means that `Reflect.get` does the job
            return Reflect.get(target, property, receiver);
          }
        },
        defineProperty(target, property, descriptor) {
          // notify about changes
          onChange();
          // use `Reflect.defineProperty` to provide default functionality
          return Reflect.defineProperty(target, property, descriptor);
        },
        deleteProperty(target, property) {
          // notify about changes
          onChange();
          // use `Reflect.deleteProperty` to provide default functionality
          return Reflect.deleteProperty(target, property);
        }
      };
    
      return new Proxy(object, handler);
    };

    And that's it for today. I can only recommend reading small modules like this one from time to time. I find useful and new stuff very often.

    Special thanks to the StackOverflow user GitaarLAB. The answer in the mentioned thread was extremely useful and I admire people that take the time to "craft" detailed answers to help people out!

  • Force pushing git branches with a safety net

    Recently, I came across the for me unknown command line flag of git push--force-with-lease. After working years with git, I feel relatively comfortable with it today. I manage to squash commits, change the last commit or rebase things on the command line with confidence.

    When you apply a lot of git magic rewriting the history of your git repository you might approach the point where you have to force push to a remote branch because it refuses to "accept your git magic".

    # lots of git magic followed by:
    
    $ git push origin foo --force 
    # ☝️ will update the remote no matter what

    Performing git push with the --force flag can be brutal. It overwrites the particular remote branch with your local version without any additional checks or asking for permission. When you're working with other people on the same branch, it can quickly happen that you overwrite commits done by your team members.

    Think of the following example:

    • you do a git pull to get the latest changes
    • you do some git magic, but it takes a while
    • you force push your changes

    Now let's assume it took you a while to perform a commit squash and another coworker pushes in the meantime new changes to the remote.

    You'll overwrite these with your force push then without knowing.

    The --force-with-lease can help to avoid this because it won't update the remote branch when it isn't in a known state.

    # lots of git magic followed by:
    
    $ git push origin foo --force-with-lease
    # ☝️ can still be rejected when
    # someone updated the branch upstream

    There a few more minor things to know about --force-with-lease and Steve Smith wrote an excellent article on the whole topic. I recommend to check that one out. ;)

Tags for this post

If you like this post share it or subscribe to my monthly newsletter.