Migrating from Ajax Minifier Version 4.x to Version 5

There are many changes internally in the version 5 release of AjaxMin. If you are simply using the tool from the command-line, or using the Microsoft.Ajax.Utilities.Minifier object’s MinifyStyleSheet and MinifyJavaScript methods, you should not have any migration issues. However, if you are using the parser, node, and visitor classes directly from the library, there are a number of breaking changes in this release.

Command-Line Usage

Working from the command-line should be the same with version 5 as it is with version 4; no changes needed.

Microsoft.Ajax.Utilities.Minifier Class

Working with the Minifier class should be the same with version 5 as it is with version 4; no changes needed.

Build Tasks

Working with any of the AjaxMin build tasks should be the same with version 5 as it is with version 4; no changes needed.

Obsolete Properties and Methods

All methods that were previously marked with the Obsolete attribute have been removed. If you were using any of these properties or methods, your code will need to migrate to the new alternative method.

Visitor Classes

If you are creating your own classes derived from IVisitor and performing operations on parsed abstract syntax trees, you will need to add a number of methods to your class to correspond to the new AST nodes:

  • void Visit(BindingIdentifier node); // a binding name associated to a field
  • void Visit(ClassNode node); // a class declaration
  • void Visit(ComprehensionNode node); // array and generator comprehension expressions
  • void Visit(ComprehensionForClause node); // comprehension for-clause
  • void Visit(ComprehensionIfClause node); // comprehension if-clause
  • void Visit(ExportNode node); // export statement
  • void Visit(ImportExportSpecifier node); // name specifier for import and export statements
  • void Visit(ImportNode node); // import statement
  • void Visit(InitializerNode node); // node representing an optional initializer for a binding
  • void Visit(ModuleDeclaration node); // module declaration
  • void Visit(TemplateLiteral node); // template literal; may contain zero or more template literal expressions
  • void Visit(TemplateLiteralExpression node); // an expression portion of a template literal

Node Constructors

Node constructors no longer require a JSParser object be passed to them. The vast majority of the classes simply take a Context object that describes where the node exists in the source code.

Node Classes

Some node classes will have no changes (other than the constructor). Others may have a couple different properties and methods. For example, the VariableDeclaration node used to have a string property, Identifier, representing the name of the variable being declared, and an AstNode property, Initializer, representing the optional initializer. With ES6 destructuring available now, the VariableDeclaration class derives from the new InitializerNode class, and it has an AstNode property, Binding, which may be a BindingIdentifier (the name part of the previous architecture), or it may be an ArrayLiteral or ObjectLiteral object. The InitializerNode is now where the AstNode property, Initializer, lives. It’s more complex than the v4 architecture, but it needs to be flexible enough to handle the ES6 destructuring syntax. Most of the node changes in this release as because of the destructuring syntax, although there were a lot of clean-up for duplicated properties and information.

Node ToCode Methods No Longer Supported

In version 4.x and earlier, if you have a reference to an AST node, you could always generate valid JavaScript code from it by calling the ToNode() method. This is no longer the case in version 5.0. Going forward, generating JavaScript code from a node reference must always be explicitly done via a node visitor, usually OutputVisitor. The Apply method on the OutputVisitor class has two overrides: one that take a TextWriter stream reference, and one that does not. Both take the node to output and a CodeSettings object. This not only gives finer control over output settings, but also allows for different output visitors to be used. For example, if you know the parsed nodes represent a JSON object (and no other nodes/structures that aren’t supported by JSON syntax), you could also use the new JSONOutputVisitor class to generate valid JSON output. This method also allows the output to be generated directly to a stream rather than to a memory string that might need to then be copied to a stream. The stream could have the appropriate JSEncoderFallback object applied to it, should \uXXXX escape sequences be required for the output. If you want the exact same equivalent to the old ToCode() method, use the override for OutputVisitor.Apply that takes the node and the settings objects and returns the generated string.

JSParser Usage

Over the years, the ways of using the JSParser object morphed until it became basically the opposite of what I would expect the class to do. In Version 4.x of the tool, one created the JSParser object by passing in a string of source code. You could optionally put a context (file name, for instance) on the parser. Then to parse the code, you called the Parse method and passed in a CodeSettings object. This made parsing multiple script parts within the same global context extremely difficult, as it required a new JSParser object for each segment, and a method of sharing the global namespace between them.

In version 5, the process has significantly changed. JSParser objects can be created with no arguments and stand alone, maintaining their global namespace. The Parse method can now be called any number of times with any number of different source code segments, and they will all be parsed into the same global namespace. The CodeSettings object can be set on the JSParse.Settings property once, or it can be specified each time Parse is called with an optional parameter. This facilitates parsing multiple segments, as in multiple scripts within a single webpage for instance: one JSParser object for the page, with multiple calls to the Parse method for each segment, each building upon the previous.

Last edited Sep 16, 2013 at 6:59 PM by ronlo, version 6


No comments yet.