Transpiles JSDoc types from namepaths with module identifiers to types for Closure Compiler.
This is useful for type checking and building an application or library from a set of ES modules with Closure Compiler, without the use of any additional bundler.
npm install babel-cli babel-plugin-jsdoc-closure
Create a .babelrc file in the root of your project to enable the plugin:
{
  "plugins": ["jsdoc-closure"],
}Note: When your code uses Closure type casts (i.e. something like /** @type {Custom} */ (foo)), you need to configure Babel to use recast as parser and generator by modifying your .babelrc file:
{
  "plugins": ["jsdoc-closure"],
  "parserOpts": {
    "parser": "recast"
  },
  "generatorOpts": {
    "generator": "recast"
  }
}You will also need to install recast to make this work:
npm install recast
To run the transform on your sources (src/) and output them to build/, run
node_modules/.bin/babel --out-dir build src
To build your project, create a simple build script (e.g. build.js) like this:
const Compiler = require('google-closure-compiler').compiler;
const compiler = new Compiler({
  js: [
    'build/**.js',
    // add directories for your dependencies, if any, here
  ],
  entry_point: 'build/index.js',
  module_resolution: 'NODE',
  dependency_mode: 'STRICT',
  process_common_js_modules: true,
  jscomp_error: ['newCheckTypes'],
  // Uncomment and modify for dependencies without Closure annotations
  //hide_warnings_for: ['node_modules']
  js_output_file: 'bundle.js'
});
compiler.run((exit, out, err) => {
  if (exit) {
    process.stderr.write(err, () => process.exit(exit));
  } else {
    process.stderr.write(out);
    process.stderr.write(err);
  }
});To run the Compiler, simply call
node build.js
Closure Compiler does not allow JSDoc's namepaths with module identifiers as types. Instead, with module_resolution: 'NODE', it recognizes types that are imported from other files. Let's say you have a file foo/Bar.js with the following:
/** @module foo/Bar */
/**
 * @constructor
 * @param {string} name Name.
 */
const Bar = function(name) {
  this.name = name;
};
export default Bar;Then you can use the Bar type in another module with
/**
 * @param {module:foo/Bar} bar Bar.
 */
function foo(bar) {}This is fine for JSDoc, and this plugin transforms it to something like
/**
 * @param {foo$Bar} bar Bar.
 */
function foo(bar) {}
const foo$Bar = require('./foo/Bar');With this, the type definition is recognized by Closure Compiler.
JSDoc uses a nice, documentable format for {Object} typedefs:
/**
 * @typedef {Object} Foo
 * @property {string} bar Bar.
 * @property {module:types.Baz} baz Baz.
 */Such typedefs are not understood by Closure compiler, so they are transformed to something like
/** @interface */
export function Foo() {};
/** @type {(string)} */
Foo.prototype.bar;
/** @type {(_types_Baz)} */
Foo.prototype.baz;Properties marked as optional with JSDoc notation are also handled. The plugin will transforms @property {number} [foo] Foo. or @property {number=} foo Foo. to foo: (undefined|number).