Skip to content

Instantly share code, notes, and snippets.

@eschwartz
Created February 5, 2015 14:44
Show Gist options
  • Save eschwartz/9876bb8166d1a98d483b to your computer and use it in GitHub Desktop.
Save eschwartz/9876bb8166d1a98d483b to your computer and use it in GitHub Desktop.
A grunt task for when you need to use RequireJS `paths` config in a TypeScript project.
module.exports = function(grunt) {
var path = require('path');
grunt.registerMultiTask(
'tsd-amd',
'Generates a TypeScript definition file for a set of *.ts files. ' +
'The output will contain declared modules for every *.ts file, ' +
'which are named according to their AMD ids.',
function() {
var options = this.options({
// path aliases
paths: {},
template: 'declare module "{amdId}" {\n' +
'\timport X = require("{requirePath}");\n' +
'\texport = X;\n' +
'}\n\n'
});
this.files.forEach(function(fileObj) {
var amdModuleDeclarations = fileObj.src.
reduce(function(str, srcPath) {
var amdId = filePathToAmd(srcPath, options.paths, '.ts');
var requirePath = withoutExt(
relativePath(fileObj.dest, srcPath),
'.ts'
);
return str + options.template.
replace('{amdId}', amdId).
replace('{requirePath}', requirePath);
}, '');
grunt.file.write(fileObj.dest, amdModuleDeclarations);
});
/**
* Convert a file path to an AMD module id.
*
* @param {string} filePath
* @param {Object} pathAliases as { 'alias-name': 'some/file/path' }
* @param {string} ext file extension
* @return {string}
*/
function filePathToAmd(filePath, pathAliases, ext) {
return Object.keys(pathAliases).
reduce(function(amdId, alias) {
var path = options.paths[alias];
var isMatchingAlias = amdId.indexOf(path) === 0;
if (isMatchingAlias) {
return amdId.replace(path, alias);
}
}, withoutExt(filePath, ext));
}
function withoutExt(path, ext) {
return path.split(ext)[0];
}
function relativePath(from, to) {
var relDir = path.relative(
path.dirname(from),
path.dirname(to)
);
return relDir ?
[relDir, path.basename(to)].join('/') :
path.basename(to);
}
}
)
;
};
@eschwartz
Copy link
Author

Example usage:

{
  'tsd-amd': {
    lib: {
      files: {
        'src/typings/myApp.d.ts': [
          'src/app/**/*.ts',
          '!src/**/*.d.ts'   // ignore existing definition files
        ],
        options: {
          paths: {
            'myApp': 'src/app',
            'lib': 'src/vendor'
          }
        }
      }
    }
  }
}

Let's say you have a file structure like:

src/
  - app/
    - MyComponent.ts
  - vendor/
    - jayKweery.ts

running `grunt ts-amd:lib' will generate a file like this:

// src/typings/myApp.d.ts
declare module "myApp/MyComponent" {
  import X = require("../src/app/MyComponent");
  export = X;
}

declare module "lib/jayKweery" {
  import X = require('../vendor/jayKweery');
  export = X;
}

Which will allow you to reference your AMD modules in a RequireJS compatible (non-relative) way:

// src/app/otherStuff/MyOtherComponent.ts

/// <reference path="../../typings/myApp.d.ts" />
require MyComponent = require("myApp/MyComponent");
require jayKweery = require("lib/jayKweery");

@eschwartz
Copy link
Author

Important note:
The reason I have not published a repo for this grunt task is that it is not really in "finished" form. That is to say, there are no tests and I have only used it under very specific conditions.

So use at your own risk, and consider this more of a jumping-off point than a completed library. As soon as I need to use it on another project, or I run into a use case I didn't account for, I'll probably break it off into its own project. If anyone wants to take a shot at adapting this into their own thing, I hereby grant you permission to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment