Converting Vue from coffeescript to javascript

July 13, 2020

This script will extract the coffee from the .vue-files, convert it with decaffeinate and then insert the javascript back into the .vue-file.

#!python

"""
Prerequisites: `npm install -g decaffeinate`.
Run from: inside `src/` folder.
Run with: `find . -type f -name '*.vue' -print0 | xargs -0 -n1 ./bulk-vue-decaf.py`
then: `npm run lint -- --fix`
"""


import os
from sys import argv
import subprocess

def get_coffee_scripttag_bounds(vue_code: str, vue_file_path: str) -> tuple:
tag_open = '<script lang="coffee">'
tag_count = vue_code.count(tag_open)
if tag_count != 1:
print("ERR: " + vue_file_path + " - number of coffee tags is not 1: " + str(tag_count))
exit(1)

start = vue_code.index(tag_open) + len(tag_open)
end = vue_code.index("</script>", start)
if start == -1 or end < start:
print("ERR: " + vue_file_path + " - (start, end) range invalid: " + start + ', ' + end)
exit(1)

return (start, end)

def extract_coffee_from_vue(vue_file_path: str) -> str:
""":return: resulting out file"""
with open(vue_file_path, "r", encoding="utf-8") as f:
code: str = f.read()

start, end = get_coffee_scripttag_bounds(code, vue_file_path)

coffeescript = code[start: end]
if len(coffeescript) == 0:
print("ERR: " + vue_file_path + " - no code!")
exit(1)

out_file = vue_file_path + ".coffee"
if os.path.exists(out_file):
print("ERR: " + out_file + " exists!")
exit(1)

with open(out_file, "w+", encoding="utf-8") as out:
out.write(coffeescript)

return out_file

def run_decaffeinate(coffee_file_path: str) -> str:
""":return: the new js file"""
result = subprocess.run(["decaffeinate", "--use-cs2", "--use-js-modules", "--loose-js-modules", coffee_file_path], shell=True, check=True, capture_output=True)
if result.returncode != 0:
print("ERR: " + coffee_file_path + " - decaffeinate gave an error. Exit code " + result.returncode)
print(result.stderr)
print(result.stdout)
exit(1)
else:
print("INFO: " + coffee_file_path + " - decaffeinated: " + result.stdout.decode("utf-8"))

return coffee_file_path.replace(".coffee", ".js", 1)

def replace_coffee_with_js_inside_vue(vue_file: str, converted_js_file: str) -> None:
with open(vue_file, "r", encoding="utf-8") as original:
code = original.read()

start, end = get_coffee_scripttag_bounds(code, vue_file)

with open(converted_js_file, "r", encoding="utf-8") as pure_js:
js_code = pure_js.read()

patched_vue_code = code[0:start] + "\n" + js_code + "\n" + code[end:]
patched_vue_code = patched_vue_code.replace('<script lang="coffee">', "<script>")

out_file = vue_file.replace(".vue", ".decaf.vue")
with open(out_file, "w+", encoding="utf-8") as out:
out.write(patched_vue_code)
return out_file

if __name__ == '__main__':
if len(argv) != 2:
print("Usage: " + argv[0] + " src/App.vue")
exit(0)

vue_file = argv[1]
if not os.path.exists(vue_file):
print("ERR: invalid file " + vue_file)
exit(1)

print("INFO: converting " + vue_file + " ...")

coffee_file = extract_coffee_from_vue(vue_file)
assert os.path.exists(coffee_file)
js_file = run_decaffeinate(coffee_file)
assert os.path.exists(js_file)
end_result_file = replace_coffee_with_js_inside_vue(vue_file, js_file)
assert os.path.exists(end_result_file)

os.unlink(coffee_file)
os.unlink(js_file)

os.unlink(vue_file)
os.rename(end_result_file, vue_file)

print("Done with " + vue_file)

Converting import/export to require/module.exports

See the GitHub gist with jscodeshift code.