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 4 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(/`|\bimport\b|\bexport\b|\byield\b/)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The following should have a noticeable performance increase over this, especially on larger files:

source.match(/`|\b(?:import|export|yield)\b/)

And if attempting to eliminate some false-positives, perhaps:

source.match(/`|(?:;|^)\h*\b(?:import|export|yield)\b/)

Though I'd venture to say that it might be possible to break this logic – haven't given it a lot of thought. My assumption here is that import/export/yield are either at the beginning of a line or after a semi-colon, proceeded by optional whitespace.

Again, any benchmarks you could run here might be nice to see. Setting the env. variable METEOR_PROFILE=50 might be your friend in seeing the differences in compilation time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

import or export by definition need to be at the beginning of a line, which in CoffeeScript means they follow a newline (no semicolons). yield would usually begin a line after some whitespace; I’m not sure if it could ever not start a line, but it might.

I can’t imagine it that takes more time to run this match than it does to recompile with Babel. Assuming most projects don’t use features that generate ES2015 code, which I think is a pretty safe assumption, this regex should be saving us time. And most projects that do generate ES2015 probably are doing so because of modules, which means this regex finds a match almost immediately, from the top of the file, so the match should be a very minor performance hit.

Copy link
Contributor

Choose a reason for hiding this comment

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

import or export by definition need to be at the beginning of a line, which in CoffeeScript means they follow a newline (no semicolons)

Not common, but CoffeeScript actually can have semi-colons. But based on your description, my second regex above should work but would need the /m modifier. Again though, this only eliminates some potential false positives.

I can’t imagine it that takes more time to run this match than it does to recompile with Babel. Assuming most projects don’t use features that generate ES2015 code, which I think is a pretty safe assumption, this regex should be saving us time. And most projects that do generate ES2015 probably are doing so because of modules, which means this regex finds a match almost immediately, from the top of the file, so the match should be a very minor performance hit.

Agree, with the same assumptions. Though I'd still recommend the first regex change regardless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So which regex would you recommend? /|\b(?:import|export|yield)\b/`?

FWIW I think import is the most likely substring that a file would contain, and most likely to be first. Backticks are probably last.

Copy link
Contributor

Choose a reason for hiding this comment

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

My vote would still be that, just for readability. The back-tick at the end is harder to see and the difference is extremely negligible that that point. 😄

// If source contains backticks or features that output as ES2015+,
// 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;