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 support for vue-cli and parcel templates #1467

Merged
merged 14 commits into from
Jan 20, 2019

Conversation

shreeve
Copy link
Contributor

@shreeve shreeve commented Jan 14, 2019

This is a first attempt at adding CoffeeScript support to the vue-cli template for Codesandbox.

@SaraVieira recommended the Parcel template first, but I couldn't get it working. I decided to try the vue-cli template, and made some progress. This is based off the work done by @zephraph on #443, but this version is not quite yet working.

I'm submitting here to see if someone with more familiarity with Codesandbox can help make whatever adjustments for CoffeeScript support.

What kind of change does this PR introduce?
Feature; adds support for CoffeeScript in vue single file components and elsewhere.

What is the current behavior?
Addresses #1384.

<script lang="coffee">
  export default

    data: ->
      name: "Bob"
      age: 34

    computed:
      retired: -> @age > 80

    methods:
      yelp: -> console.log "Hey!"
</script>

What is the new behavior?
.coffee files will be supported generally and in <script lang="coffee"> sections of .vue files.

Checklist:

  • Documentation - not much to add, just use it as normal
  • Tests - same
  • Ready to be merged - lgtm
  • Added myself to contributors table - done

@shreeve
Copy link
Contributor Author

shreeve commented Jan 14, 2019

I have a .vue file that looks like this:

<script lang="coffee">

  import HelloWorld from "./components/HelloWorld"

  export default
    name: "App"
    components: { HelloWorld }

</script>

It does correctly get compiled by the CoffeeScript compiler and then injected into some code to eval.

But, the eval code looks like this:

(function evaluate(require, module, exports, process, setImmediate, global, afterAll, afterEach, beforeAll, beforeEach, describe, it, test, expect, jest, __dirname, __filename) {
  
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  components: {HelloWorld}
};

})

The following error is shown when it runs:

image

It sort of like the movie "Inception" to me, so I'm not really sure in which context there is an unexpected identifier.

Is it complaining about the import statement? Or is something else fishy?

literate: false,
inlineMap: true,
sourceMap: false,
transpile: false,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is where we are supposed to supply the transpiler details. Something like this:

      transpile: {
        transpile: require('@babel/core'), // this doesn't work, since no require()
        presets: ['@babel/env'], // this doesn't work, what should it say?
      },

Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't it make sense to only use coffeescript for the transpilation from .coffee to .js and then run the Babel transpiler on that output?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CompuIves - Yes, I just couldn't figure out how to do it.

@@ -151,6 +152,9 @@ export default function initialize() {
vuePreset.registerTranspiler(m => m.path.endsWith('pug'), [
{ transpiler: pugTranspiler },
]);
vuePreset.registerTranspiler(m => m.path.endsWith('coffee'), [
{ transpiler: coffeeTranspiler },
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be solved by appending

{ transpiler: babelTranspiler }

to the array!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I will try to get this to work!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I tried this but it didn't work. Instead, I had to add const postLoaders = { coffee: 'babel-loader' }; to transpilers/vue/loader.js, which made it all work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried this, but it didn't work. I added const postLoaders = { coffee: 'babel-loader' }; to eval/transpilers/vue/loader.js and it worked!

@@ -148,9 +149,13 @@ export default function initialize() {
{ transpiler: noopTranspiler },
]);
vuePreset.registerTranspiler(() => true, [{ transpiler: rawTranspiler }]);
vuePreset.registerTranspiler(m => m.path.endsWith('pug'), [
vuePreset.registerTranspiler(module => /\.pug$/.test(module.path), [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the pug section also, just so all sections in this file use the same syntax.

bare: true,
literate: false,
inlineMap: true,
sourceMap: false,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the transpile: ... entry here.

};

const loaders = Object.assign({}, defaultLoaders, codeSandboxLoaders);
const preLoaders = {};
const postLoaders = {};
const postLoaders = { coffee: 'babel-loader' };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seemed like the clencher... only by having this could I get the <script lang='coffee'> sections of the .vue files to work.

@shreeve
Copy link
Contributor Author

shreeve commented Jan 15, 2019

This PR seems to make it so that <script lang='coffee'> works in .vue files.

However, when I import *.coffee files from the file system, I see something like this:

(function evaluate(require, module, exports, process, setImmediate, global, afterAll, afterEach, beforeAll, beforeEach, describe, it, test, expect, jest, __dirname, __filename

) {
      module.exports = "alert \"Hello world!\"";
//# sourceURL=http://localhost:3002/src/top.coffee
})

What is needed to be able to import *.coffee files using import 'foo.coffee'?

@shreeve shreeve closed this Jan 15, 2019
@shreeve shreeve reopened this Jan 15, 2019
@shreeve
Copy link
Contributor Author

shreeve commented Jan 15, 2019

image

@@ -39,7 +40,7 @@ const getFileNameFromVm = vm => {
export default function initialize() {
const vuePreset = new Preset(
'vue-cli',
['vue', 'json', 'js', 'ts'],
['vue', 'json', 'js', 'ts', 'coffee'],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is adding coffee necessary here? Seems to work with or without it.

Copy link
Member

Choose a reason for hiding this comment

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

It's used to allow import './test' to resolve to test.coffee. I think it would be useful 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, cool.

@@ -127,6 +128,9 @@ export default function initialize() {
vuePreset.registerTranspiler(module => /\.vue$/.test(module.path), [
{ transpiler: vueTranspiler },
]);
vuePreset.registerTranspiler(module => /\.coffee$/.test(module.path), [
{ transpiler: coffeeTranspiler },
]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I originally added this coffee transpiler to the end of the file, but realized that it was not being executed since the level or declaration determines its priority and it was lower than the raw-loader.

import { buildWorkerError } from '../utils/worker-error-handler';

self.importScripts(
`${process.env.CODESANDBOX_HOST || ''}/static/js/coffeescript.2.3.2.js`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be better to pull this from a CDN instead of including this always?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I think it doesn't really matter where we get it from. CDN works too, might be a bit faster even, but not noticeably I think.

@shreeve
Copy link
Contributor Author

shreeve commented Jan 15, 2019

@SaraVieira, @CompuIves - Does everything seem ok? If so, I believe it will be easy to add support for the parcel template also.

@shreeve shreeve changed the title CoffeeScript support for vue-cli template CoffeeScript support for vue-cli and parcel templates Jan 16, 2019
@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

Once coffeescript support was in for vue-cli, it was just a small tweak to add support for parcel also. So, I just added it in.

worker: Worker;

constructor() {
super('coffee-loader', CoffeeWorker, 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does this 1 mean the number of concurrent workers? Should we change it or keep it at 1?

Copy link
Member

Choose a reason for hiding this comment

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

Yep! I think 1 is enough for the amount of coffee files in a project (since node_modules doesn't have coffee files it should be a low amount)

@@ -122,6 +122,7 @@ export default function(content: string, loaderContext: LoaderContext) {
ts: ['ts-loader'],
typescript: ['ts-loader'],
pug: ['pug-loader'],
coffee: ['babel-loader', 'coffee-loader'],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why is the 'babel-loader' needed to make this work?

Copy link
Member

Choose a reason for hiding this comment

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

babel-loader also checks all the require statements and adds them to the dependency graph. So this is required to make import statements work properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, wow. I'm glad I asked! :-)

@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

@SaraVieira, @CompuIves - I think this is ready for a review. I can launch a new vue-cli or parcel codesandbox and add *.coffee files or add <script lang='coffee'> sections to *.vue files and it works right away.

@shreeve shreeve closed this Jan 16, 2019
@shreeve shreeve reopened this Jan 16, 2019
@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

I see that some checks in ci/circleci: test-integrations are failing, but I'm not sure how to verify or fix those.

@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

Should address #1384 - CoffeeScript not working

@SaraVieira
Copy link
Contributor

I see that some checks in ci/circleci: test-integrations are failing, but I'm not sure how to verify or fix those.

It's also failing on master :(

Don't worry, that is on us

@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

@SaraVieira - Ok, cool

@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019

@SaraVieira, @CompuIves - If this PR looks good and gets merged, how long does it usually take before this version of the repo is deployed on the live codesandbox.io site?

@SaraVieira
Copy link
Contributor

Hey!

Our deploys are ad-hock so I would say one or two days max

@shreeve
Copy link
Contributor Author

shreeve commented Jan 16, 2019 via email

@CompuIves
Copy link
Member

This looks really good! Exactly the way I would implement it as well. I'm going to do some testing today and merge it in if it works properly!

@AlexGrafe
Copy link

At the end of all of this, could someone summarize what to do as a normal user to get CoffeeScript working on parcel ?
Thanks to everyone involved. This is amazing!

@AlexGrafe
Copy link

@GeoffreyBooth: CoffeeScript working soon on CodeSandbox done by @shreeve

@GeoffreyBooth
Copy link

Congrats! Thanks @shreeve!

@shreeve
Copy link
Contributor Author

shreeve commented Jan 18, 2019

I made a quick screencast of how this works with the vue-cli template:

https://youtu.be/eHxDezrYRa4

@shreeve
Copy link
Contributor Author

shreeve commented Jan 18, 2019

Here's another showing how to support the parcel template:

https://youtu.be/12d13-s-JBQ

@shreeve
Copy link
Contributor Author

shreeve commented Jan 18, 2019

main.coffee

import Vue from "vue"
import App from "./App"

Vue.config.productionTip = false

new Vue
  el: "#app"
  render: (h) -> h App

App.vue

<template lang="pug">
  .example
    h3 Important Stuff
    p Maybe we should start with some {{ active }}...
    p How about:
    ul: li(v-for='item in this[active]') {{ item.join(', by ') }}
    button(@click='surprise') Surprise Me!
</template>

<script lang="coffee">
export default
  data: ->
    active: 'jokes'
    genres:
      jokes: 'funny jokes'
      poems: 'beautiful poems'
      thoughts: 'deep thoughts'
    jokes: [
      [ 'How many foos does it take', 'my son Sailor' ]
      [ 'Knock, knock...', 'my friend Batman' ]
      [ 'So, this guy walks into a bar', 'your crazy neighbor' ] ]
    poems: [
      [ 'The Gods of the Copybook Headings', 'Rudyard Kipling' ]
      [ 'Daffodils', 'William Wordsworth' ]
      [ 'Fire and Ice', 'Robert Frost' ] ]
    thoughts: [
      [ 'Time is relative', 'Albert Einstein' ]
      [ 'The quintic is unsolveable', 'Evariste Galois' ]
      [ 'Frankly my darling...', 'Rhett Butler' ] ]
  methods:
    pick: (ary) ->
      ary[Math.floor(Math.random() * ary.length)]
    surprise: ->
      list = Object.keys @genres
      item = @active
      item = @pick(list) until item isnt @active
      @active = item
</script>

<style lang="stylus">
  .example
    font-family: Avenir
</style>

@AlexGrafe
Copy link

Thanks @shreeve. This is amazing! Thanks for all the digging and work!

@shreeve
Copy link
Contributor Author

shreeve commented Jan 19, 2019

@CompuIves / @SaraVieira - I'm not sure if there is some kind of CI check that needs to be fixed prior to this being merged, or if that top CircleCI issue is "no big deal". Is this PR able to merged or not quite yet? Thanks!

@CompuIves
Copy link
Member

Great! Tested it and everything works! Merging in now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants