Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CoffeeScript now supports ES2015 modules #7818

Merged
merged 5 commits into from
Sep 28, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions packages/coffeescript/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Package.registerBuildPlugin({
use: ['caching-compiler', 'ecmascript'],
sources: ['plugin/compile-coffeescript.js'],
npmDependencies: {
"coffee-script": "1.10.0",
"coffee-script": "1.11.0",
"source-map": "0.5.6"
}
});
Expand All @@ -18,10 +18,10 @@ Package.onUse(function (api) {
api.use('babel-compiler');

// Because the CoffeeScript plugin now calls
// BabelCompiler.prototype.processOneFileForTarget for any raw
// JavaScript enclosed by backticks, it must provide the same runtime
// environment that the 'ecmascript' package provides. The following
// api.imply calls should match those in ../ecmascript/package.js,
// BabelCompiler.prototype.processOneFileForTarget for any ES2015+
// JavaScript or JavaScript enclosed by backticks, it must provide the
// same runtime environment that the 'ecmascript' package provides.
// The following api.imply calls should match those in ../ecmascript/package.js,
// except that coffeescript does not api.imply('modules').
api.imply('ecmascript-runtime');
api.imply('babel-runtime');
Expand All @@ -37,6 +37,7 @@ Package.onTest(function (api) {
'tests/coffeescript_test_setup.js',
'tests/coffeescript_tests.coffee',
'tests/coffeescript_strict_tests.coffee',
'tests/coffeescript_module.coffee',
'tests/es2015_module.js',
'tests/litcoffeescript_tests.litcoffee',
'tests/litcoffeescript_tests.coffee.md',
Expand Down
5 changes: 3 additions & 2 deletions packages/coffeescript/plugin/compile-coffeescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ export class CoffeeCompiler extends CachingCompiler {

let sourceMap = JSON.parse(output.v3SourceMap);

if (source.indexOf('`') !== -1) {
// If source contains backticks, pass the coffee output through babel-compiler
if (source.match(/(`|import|export|function\*)/)) {
Copy link
Contributor

@abernix abernix Sep 26, 2016

Choose a reason for hiding this comment

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

If I'm not mistaken, source is the contents of a CoffeeScript file (not JavaScript) and CoffeeScript doesn't have the function* generator syntax. Per CoffeeScript docs, they refer to it as "nonsense":

CoffeeScript functions also support ES6 generator functions through the yield keyword. There's no function*(){} nonsense — a generator in CoffeeScript is simply a function that yields.

Therefore, this should be looking for yield instead of function*, no?

Copy link
Contributor

@abernix abernix Sep 26, 2016

Choose a reason for hiding this comment

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

Also, this regex could be improved (for example, with word boundaries so it doesn't match, say, a comment about reexportation) but it might be futile trying to match the CoffeeScript syntax accurately and efficiently.

Perhaps it would be better to just always pass it to babel-compiler? I'm not sure what the original thinking behind the conditional babel compilation was.

At the very least, if this stays there's no need for the outer parenthesis (as there is no capture) which just add extra, potentially-expensive steps for the regex engine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch re function*. You are correct, it should be yield.

As for whether to use the regex at all . . . originally this was only checking for backticks, on the assumption that few if any CoffeeScript files had backticks. My Todos CoffeeScript example, for instance, has none. So checking for at least one backtick before recompiling the file through Babel was a performance improvement: only the very few files that needed it would get the second pass.

Now that the check is looking for import and export, many more files will get passed through Babel. Presumably the regex will short-circuit the moment it finds the first hit, and import statements are generally at the top of a file, so this regex should be quite inexpensive in most cases. But if most CoffeeScript files pass the test and get sent through Babel, then the regex might be hurting performance: basically, is the cumulative time spent running this regex for every file greater than the time saved by sparing a few files from double compilation?

My instinct is that the regex is still probably a net time saver, at least for now. There are probably still a lot of projects out there that don’t use modules, especially CoffeeScript projects. But I don’t feel strongly about it, if you think otherwise. I definitely think the check should be removed in a future release, probably at the next release of CoffeeScript.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a good reproduction where I could reliably benchmark the two scenarios right now (perhaps that's something you might be in a better position to test?), but right now this would likely be adding a small amount of time (dependent on file-length) to most CoffeeScript projects because (as you said) most CoffeeScript projects will not be using modules right now and because of that, there will not be the ability to short-circuit early as each file will have to be read to completion. Unfortunately, that's just a trade off that'll be necessary to enable this module support.

// If source contains backticks or ES2015+ features,
// pass the coffee output through babel-compiler
const doubleRoastedCoffee =
this.babelCompiler.processOneFileForTarget(inputFile, output.js);

Expand Down
1 change: 1 addition & 0 deletions packages/coffeescript/tests/coffeescript_module.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export testingForNativeImportedModule123456789 = true
22 changes: 18 additions & 4 deletions packages/coffeescript/tests/coffeescript_tests.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@ Tinytest.add "coffeescript - compile", (test) -> test.isTrue true


# import/export statements must be top-level
`import { Meteor as testingForImportedSymbol123456789 } from "meteor/meteor";`
Tinytest.add "coffeescript - import external package via backticks", (test) ->
test.isTrue testingForImportedSymbol123456789?
`import { Meteor as testingForBacktickedImportedSymbol } from "meteor/meteor";`
Tinytest.add "coffeescript - import external package via backticked import statement", (test) ->
test.isTrue testingForBacktickedImportedSymbol?

`import { testingForImportedModule987654321 } from "./es2015_module.js";`
Tinytest.add "coffeescript - import local module via backticks", (test) ->
Tinytest.add "coffeescript - import local module via backticked import statement", (test) ->
test.isTrue testingForImportedModule987654321?


import { Meteor as testingForNativeImportedSymbol } from "meteor/meteor"
Tinytest.add "coffeescript - import external package via native import statement", (test) ->
test.isTrue testingForNativeImportedSymbol?

import { testingForImportedModule123456789 } from "./es2015_module.js";
Tinytest.add "coffeescript - import local module via native import statement", (test) ->
test.isTrue testingForImportedModule123456789?


import { testingForNativeImportedModule123456789 } from "./coffeescript_module.coffee";
Tinytest.add "coffeescript - import local module exported by a CoffeeScript native export statement, via native import statement", (test) ->
test.isTrue testingForNativeImportedModule123456789?
1 change: 1 addition & 0 deletions packages/coffeescript/tests/es2015_module.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const testingForImportedModule123456789 = true;
export const testingForImportedModule987654321 = true;