Skip to content

markeissler/typescript-monkey

Repository files navigation

TypeScript Monkey Build Status Coverage Status

This is a TypeScript transpiler engine for the Rails asset pipeline.

BETA: TypeScript Monkey is currently in pre-release. That doesn't mean it's not ready for production, it just means it hasn't been tested by a large audience yet. The more the merrier and the faster we get to v1.0. Install it, open issues if you find bugs.

Overview

TypeScript Monkey let's you use TypeScript (files with a .ts extension) wherever you can use JavaScript (.js) files. This includes support for pre-processing other template formats. For instance, you can also create .ts.erb files. In essence, TypeScript is a first class citizen on Rails!

For more information on TypeScript visit the TypeScript homepage.

Requirements

Node.js

One of the goals of TypeScript Monkey is to reduce the dependence on third-party gems which aren't under active development. This gem does require that Node.js has been installed either globally or locally. Specifically, TypeScript support (language and compiler) is provided by the TypeScript package for Node. If installed locally, it is not recommended that you commit your node_modules directory to source control.

If you're not familiar with Node.js don't worry, it's not that scary.

To install Node.js support, our recommendation is to use an appropriate package manager:

Installing Node.js via package manager

Recommended package managers

For MacOS, our recommendation is Homebrew; for Windows your best bet is probably Chocolatey. Go and install a package manager first, then install node, then come back here. If you're on Linux, well, the assumption is that you probably know what you're doing already!

NOTE: Another obvious choice for Windows is NuGet but it appears that the Node package there is stale.

Installation

Adding a Node Package Definition (package.json) File

The first step is to create a Node.js package.json file at the root of your Rails project. You may have created this file already for other uses, if so, then you will need to merge in the following dependencies:

  "dependencies": {
    "@types/jquery": "^2.0.41",
    "@types/node": "^7.0.14",
    "typescript": "^2.3.1"
  }

If you don't have an existing package.json you can use the example_package.json file in the contrib directory as a starting point. Copy the file to the root of your Rails project. Be sure to rename the file as simply "package.json".

The second step is to install some TypeScript dependencies with the Node.js package manager (npm):

    >npm install

The package manager will determine and install the dependencies noted above by reading the package.json file.

Adding the Gem to your Rails Project's Gemfile

Update your Gemfile to include the following statement:

# typescript support in the asset pipeline!
gem 'typescript-monkey', '~> 0.9.0'

Then run bundler:

    >bundle install

Finally, restart your Rails server to load the Typescript Monkey gem.

Usage

TypeScript Triple-Slash Directives

Typescript Monkey parses Typescript files and examines Triple-Slash directives (also known as reference tags or comments). For each directive, a Sprockets dependency relationship will be created so that a change in a dependency will trigger Sprockets to re-process the dependent file as well.

NOTE: TypeScript Monkey does not currently support import directives. See Features Not Yet Implemented (but on the way) for more information.

Learn more about Triple-Slash directives by referring to the following secction of the Typescript Spec:

Source Files Dependencies

In the Asset Pipeline (Sprockets)

Typical usage is the same as with JavaScript. That is, you will need to add your TypeScript files to .js manifest files just like you would with JavaScript. For instance, in the below example let's pretend that you have created the following:

app/assets/javascripts/
├── application.js
├── my-typescript-files
    └── superduper.ts

File example: superduper.ts

class SuperDuper {
    public message(message: string): void {
        const body: HTMLBodyElement = document.getElementsByTagName("body")[0];
        const element = document.createElement("p");
        element.innerHTML = `${message}`;
        body.appendChild(element);
    }
}

$(() => {
    const superduper = new SuperDuper();
    superduper.message("TypeScript at work.");
});

Then, your application.js file might look like this:

File example: application.js

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require jquery-ui/core
//= require bootstrap-sprockets
//= require my-typescript-files/superduper

Embedded in <script> tags

Yes, you can embeded TypeScript in your templates just like JavaScript! All that's required is TypeScript Monkey DYnamic Runtime Transpiler (aka "dyrt"). For example, let's say you had the following snippet in your template:

    <script type="text/typescript">
        class Alert {
            public alert(message: string): void {
                alert(`An important message: ${message}`);
                return;
            }
        }
        let myAlert = new Alert();
        myAlert.alert("TypeScript is awesome!");
    </script>

The key is making sure your <script> tag type is set appropriately as above.

To transpile this script at runtime you will have to make dyrt available at runtime. You will also need to make the TypeScript library avaialable at runtime. Both of these dependencies can be resolved by adding the following to the end of your <body> section:

    <%= javascript_include_tag 'transpiler_pkg' %>

After the page loads, dyrt will parse all <script> tags, transpile the TypeScript objects and then append them to the end of the page.

Loading the transpiler with other scripts

Yes, you could (theoretically) load the Typescript Monkey transpiler along with you other script assets. If you were to do that, you would then have to make the following call to trigger transpilation after DOM load:

$(function() {
    var transpiler = new TypeScript::Monkey::Transpiler();
    transpiler.transpile();
);

The transpile() method will parse the page, remove all previous transpiled scripts, transpile each TypeScript object and then append these scripts to the end of the page.

Pretty neat right? BUT it's important to state that using TypeScript Monkey this way may not offer the best peformance. Also, you're inline scripts will not benefit from compression or minification. (The transpiler_pkg will, however, be minified by Sprockets as usual).

Updating Files and Clearing the Sprockets Cache

You've updated some of your TypeScript files and for some reason the changes aren't appearing. Hate it when that happens, right? Do this:

    >rake tmp:clear

Then re-load your website. Sprockets will re-compile all of your TypeScript assets.

Configuration

App-level Configuration

The common application level configuration options that affect the TypeScript Monkey gem are:

    #
    # Rails options...
    #
    config.app_generators.javascript_engine :typescript
    #
    # Sprockets options...
    #
    config.assets.enabled = true
    config.assets.js_compressor = :uglifier
    # set to "true" to disable Sprockets concatenation
    config.assets.debug = false
    # set to "true" to generate digests
    config.assets.digest = true

Since the above configuration options appear at the "app" level, changing these settings can affect gems in addition to the Typescript Monkey gem.

Setting Typescript as the Default Javascript Engine

If you plan on using TypeScript in your project exclusively (instead of plain .js or .coffee files), then you might want to configure rails so that generators produce TypeScript template files by default. Add the following lines to your config/application.rb file.

module MyRailsApp
  class Application < Rails::Application
    ...
    # enable typescript in the asset pipeline
    config.app_generators.javascript_engine :typescript
    # enable the asset pipeline (!! Make sure assets are enabled !!)
    config.assets.enabled = true
    ...
  end
end

Alternatively, you can specify the JavaScript engine as a parameter to the rails generator command line:

    >rails generate controller MyController --javascript_engine=typescript

Gem-level Configuration

The TypeScript Monkey gem offers some level of configuration. These settings should be configured from within an appropriate initializer. An example_typescript.rb initialization file can be found in the contrib directory. Copy the file to the app/config/initializers directory of your Rails project. Be sure to rename the file as simply "typescript.rb".

Setting Traditional TypeScript Concatenation

During TypeScript transpilation (conversion of TypeScript to JavaScript), the TypeScript compiler will resolve triple-slash directives (dependencies) and as a result transpile and concatenate the dependencies to each file as needed. The upside is that each indivual file will contain its dependencies; the downside is that this usually results in code duplication in a Rails environment.

Typescript Monkey procedes with transpilation under the notion that all dependencies will be met at runtime. That should be the case if you have setup your manifest files correctly (either the single application.js manifest file or your own additional manifest files).

Still, if you have a reason to revert back to TypeScript's concatenation scheme then you can do so by enabling the following Typescript Monkey configuration in an initializer:

Typescript::Monkey.configure do |config|
  # Configure Typescript::Monkey concatenated compilation
  config.compile = true
end

Configuring a Logger

Adding a logger config is really only useful for debugging. Don't configure logging unless you fully understand the paragraph that follows the configuraiton snippet:

Typescript::Monkey.configure do |config|
  # Configure Typescript::Monkey logging (for debugging your app build)
  config.logger = Rails.logger
end

In general, you will see a lot of dependency "errors" generated by the logger. These "errors" (again, in quotes) are passed through from the TypeScript compiler which doesn't understand that we're building a web site and that all of the "missing" (also, in quotes) dependencies will be resolved at runtime!

Features Not Yet Implemented (but on the way)

This is a short-list of planned features:

  1. Support for specifying compiler options from initializer.
  2. Support for reading compiler options from tsconfig.json.
  3. Support for "import" syntax.
  4. Features I forgot to write down and can't think of right now...

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Authors

TypeScript Monkey is the work of Mark Eissler based on the original work of others as noted in the Attributions section below.

Attributions

TypeScript Monkey is a fork of typescript-rails. A lot of work has gone into producing Typescript Monkey which offers better support for Sprockets and the way that Rails works with assets to provide a more native and familiar experience. Still, without the prior work of Klaus Zanders ([email protected]) and FUJI Goro ([email protected]) and also the coffee-rails project, this gem would probably not exist.

License

Typescript Monkey is licensed under the MIT open source license.


Without open source, there would be no Internet as we know it today.

About

TypeScript adapter for the Rails asset pipeline.

Resources

License

Stars

Watchers

Forks

Packages

No packages published