Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Add static has build optimization #172

Merged
merged 12 commits into from
Aug 30, 2017
Merged

Add static has build optimization #172

merged 12 commits into from
Aug 30, 2017

Conversation

kitsonk
Copy link
Member

@kitsonk kitsonk commented Jul 3, 2017

Type: feature

The following has been addressed in the PR:

  • There is a related issue
  • All code matches the style guide
  • Unit or Functional tests are included in the PR

Description:

This PR allows build optimization using the has feature detection library. During the build process, any static features that have been asserted are used to replace call expressions that look something like has('feature'). For example, if feature foo was set to true as a static flag, this code:

import has from `@dojo/has/has`;

if (has('foo')) {
  console.log('has foo');
}
else {
  console.log('does not have foo');
}

Would be transformed to this:

import has from `@dojo/has/has`;

if (true) {
  console.log('has foo');
}
else {
  console.log('does not have foo');
}

Then further during the build process, if the build is being minified, uglify will detect the dead code and rewrite it further to something like:

console.log('has foo');

At the command line, users are abstracted from individual flags, and instead supply a set of feature flags they wish to use. The supported sets are located in src/features/*.json. These are specified by the user by setting the -f or --feature flag on the CLI. Multiple feature sets are combined to produce the largest set where the values do not conflict. For example es6-promise is true on chrome but false on ie11. If chrome and ie11 were specified, the values would conflict and this feature would not be statically bound.

A user supplying multiple platforms would look something like this:

$ dojo build -f chrome safari firefox

Which would produce a build that is statically built with features that are common between Chrome, Safari, Firefox.

At the moment, most of the shim library isn't written to have polyfill code within has() guard blocks, therefore it doesn't get removed from the build when the native feature is statically expressed. We will need to revisit those modules in order to create something that builds optimally.

Todo:

  • - test cases
  • - readme/documentation

Resolves #142

@kitsonk kitsonk changed the title WIP: Add static has build optimization Add static has build optimization Jul 4, 2017
@kitsonk
Copy link
Member Author

kitsonk commented Jul 4, 2017

Two thoughts on this...

  • In the original issue, I suggested -t and --target. I am not sure which makes overall better sense. Or even something else?
  • There is no support for user defined flags. I think that is fairly critical, but I suggest we tackle that as a separate issue/PR after we land this one. I think using .dojorc to specify custom features which will be mixed in to any other targets makes the most sense.

dylans
dylans previously requested changes Jul 4, 2017
Copy link
Member

@dylans dylans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor documentation nits/improvements

README.md Outdated
@@ -79,6 +80,33 @@ If the source file does not follow the pattern `create[custom element]Element`,
dojo build webpack --element=src/path/to/element.ts --elementPrefix=the-special
```

### Feature optimization

This command supports the ability to optimize code based on statically asserted features. The tool can search the source code for modules that attempt to detect features using a [`@dojo/has`](https://github.com/dojo/has) type of API. By supplying a feature set (or sets) on the command line, the build will be optimized to optimize code branches, making the code smaller and slightly more efficient. This allows targeting of particular platforms. The tool will not optimize code, when specifying multiple feature sets, they do not align. It will instead continue to leave that feature to be detected at run-time.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the build will be optimized to optimize code branches, making the code smaller and slightly more efficient. -> the build will optimize code branches, making the code smaller and more efficient.

README.md Outdated
@@ -79,6 +80,33 @@ If the source file does not follow the pattern `create[custom element]Element`,
dojo build webpack --element=src/path/to/element.ts --elementPrefix=the-special
```

### Feature optimization

This command supports the ability to optimize code based on statically asserted features. The tool can search the source code for modules that attempt to detect features using a [`@dojo/has`](https://github.com/dojo/has) type of API. By supplying a feature set (or sets) on the command line, the build will be optimized to optimize code branches, making the code smaller and slightly more efficient. This allows targeting of particular platforms. The tool will not optimize code, when specifying multiple feature sets, they do not align. It will instead continue to leave that feature to be detected at run-time.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to a separate paragraph and change "The tool will not optimize code, when specifying multiple feature sets, they do not align. It will instead continue to leave that feature to be detected at run-time." to "When specifying multiple feature sets, if they do not align, the tool will not optimize the source code for these feature sets and will instead continue to leave that feature to be detected at run-time."

README.md Outdated
|`node`|This feature set represents Node.js 7+[<sup>2</sup>](#note-2)|
|`safari`|This feature set represents Safari 10+[<sup>2</sup>](#note-2)|

<span id="note-1">[1]:</span> Many of these features were present in earlier versions, but the specific version was the GA release at the time of writing when this was validated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also say something to the effect that this isn't specific to Chrome 59+ or Opera 46+, and also supports browsers built on the same codebase such as Brave, Vivaldi, and perhaps Electron?

README.md Outdated

From the command line, the feature sets are provided to the `-f` or `--feature` argument. The available feature sets are aligned to platforms. The currently available feature sets are:

|Feature Set|Description|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People may not be impressed to learn that we have what they might think of a browser sniff testing rather than feature testing, so perhaps it's worth explaining a bit more how these feature test lists were created?

return {};
}

// Reduce the array of loaded features to the largest set of features where the values don't
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't -> do not

const features = this._features;
const dynamicFlags = new Set<string>();

// setup the dependencies for the subtitution
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subtitution -> substitution

compiler.plugin('compilation', (compilation, data) => {
// and we want to hook the parser
data.normalModuleFactory.plugin('parser', (parser) => {
// we need direct access to the AST to properly figure out the has subtitution
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subtitution -> substitution

/**
* Walk a estree AST tree
* @param node The root estree Node to start walking
* @param param1 An object with an `enter` and `leave` properties
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with an -> with

@dylans dylans added this to the 2017.07 milestone Jul 4, 2017
@kitsonk
Copy link
Member Author

kitsonk commented Jul 7, 2017

This now requires dojo/shim#101 for the sets of features to actually be effective.

});

if (leave) {
leave(node, parent, prop, index);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not important, since it doesn't use shouldSkip, but it feels like leave should be called with the same context as enter? Just for consistency maybe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but we don't want people calling this.skip() thinking that it would do something. So instead I have changed it to null and annotated the this in the types so that no one gets any bright ideas.

@kitsonk kitsonk merged commit 69b6213 into dojo:master Aug 30, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants