Dealing with macOS aliases on the command-line

If you’ve been using a Mac for a while, you’ve probably come up against Aliases. They’re similar in many ways to the Unix Symbolic Link, but have their own set of advantages and disadvantages. The main advantage is you can move the original file to a new location on the same disk, and the alias will still find it. The main disadvantage (for me) is that the OS X Unix layer doesn’t know what to do with them.

We use aliases in Papers for various reasons, and I needed a way to handle them from the node.js world. For that reason, I wrote a small module called Alias Resolver. I recently updated the module to support the creation of aliases (mainly because it is useful for testing). As a side-effect of this, the module is now usable for creating and resolving aliases on the command-line.

To create an alias in the shell, you may have used some funky-looking AppleScript to tell the Finder to do it for you. Now, it’s much simpler (assuming you have Homebrew or Node.js), as Alias Resolver comes with the create-alias and resolve-alias commands.

  $ brew install node
  $ npm install -g alias-resolver
  $ create-alias /bin/ls /tmp/ls-alias
  $ file /tmp/ls-alias
  /tmp/ls-alias: MacOS Alias file
  $ cat /tmp/ls-alias
  bookmark88<'Atbinv8A    file:///
                                  Macintosh H(A&$FE16297F-ACA3-31D2-88EF-3B67691353C9/0dnib(Xx@h (      0 4"<%
  $ resolve-alias /tmp/ls-alias
  /bin/ls

Creating an NPM module in TypeScript

I’m a great believer in type systems and we’ve recently started to use TypeScript. We want to mix TypeScript modules into our existing node.js code. However, TypeScript is a rapidly changing ecosystem, which meant I went down some blind alleys when trying to figure out how to set up an npm module. It turns out, with TypeScript 2.0, it’s really simple. Here’s one suggestion for how to get started. Begin with a directory for your project and

npm init

Set your entry point to dist/${projectName}.js. For instance, for a project called safeLeftPad, put this in package.json:

{
  …
  "main": "dist/safeLeftPad.js",
  …
}

You’re going to need to install TypeScript if you haven’t already. Let’s install it globally, and in the project so random JavaScript hackers can still compile our module.

npm install --save-dev typescript
npm install --global typescript

Typescript uses a tsconfig.json file to configure the compiler. Let’s autogenerate one:

tsc --init

You need to edit it to output to the correct directory (dist) and set the source location. You only need to point it at the main entry point file. I also turn on noImplicitAny to keep me honest, and target es6 to get the cool stuff. My tsconfig.json now looks like this:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": true,
        "sourceMap": false,
        "outDir": "dist"
    }, 
    "files": [
      "src/safeLeftPad.ts"
    ]
}

Add tsc as a prepublish command to package.json so the TypeScript is compiled when you package. My package.json now looks like this:

{
  "name": "safe-left-pad",
  "version": "0.0.1",
  "description": "",
  "main": "dist/safeLeftPad.js",
  "scripts": {
    "prepublish": "tsc",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Ben Blackburne",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^2.0.3"
  }
}

To compile your TypeScript, simply run tsc and the appropriate .js and .d.ts files are created in dist/. You don’t need autodts.

A word about typings

The first thing you are probably going to do is try and use some node modules. To do so, you need the appropriate Declaration Files for the module, which contain the type definitions. There’s a bunch of pre-existing projects for this (e.g. typings, DefinitelyTyped). However, it is now all integrated into npm and you need (in theory) do nothing more than npm install @types/${moduleName}. I’ve come across a couple of gotchas though.

Firstly, the type definitions won’t necessarily match the module version, and npm won’t care. For instance:

npm install request-promise @types/request-promise --save

At time of writing, this will happily install request-types version 4, and the definitions for version 3. In this case, I suggest you manually use the older version of request-promise:

npm install request-promise@3.0.0 @types/request-promise@3.0.0 --save

Secondly, it doesn’t seem to handle dependencies well. On compiling a project, you may find an error about missing dependencies:

node_modules/@types/request-promise/index.d.ts(10,30): error TS2307: Cannot find module 'bluebird'.

So you have to manually install the missing definitions:

npm install @types/bluebird --save