Skip to content

Instantly share code, notes, and snippets.

@DanielKoehler
Created November 28, 2016 17:22
Show Gist options
  • Select an option

  • Save DanielKoehler/a94134fd8afe557b702e443026cd3f08 to your computer and use it in GitHub Desktop.

Select an option

Save DanielKoehler/a94134fd8afe557b702e443026cd3f08 to your computer and use it in GitHub Desktop.
A really horrible (but functioning) script that will try to convert ES6 style imports in a project to require() style syntax.
import re
from os import path
import click
import json
importExpression = r""".*import[ \t]+(?P<varname>[a-zA-Z0-9*_ ]+,*\s*)*({\s*(?P<module>[a-zA-Z0-9_*\s,]+)*\s*})*[ \t]+from[ \t]+(?P<br>['"])(?P<path>[a-zA-Z0-9-_\.\/]+)(?P=br).*"""
def lookahead(iterable):
# Get an iterator and pull the first value.
it = iter(iterable)
last = next(it)
# Run the iterator to exhaustion (starting from the second value).
for val in it:
# Report the *previous* value (more to come).
yield last, True
last = val
# Report the last value.
yield last, False
def require_from_module_default(direct_import_string, destructured_import_string, path):
direct = []
destructured = []
suffixed = []
definition= []
if direct_import_string:
direct_imports = direct_import_string.strip().split(",")
[direct.append(direct_import) for direct_import in direct_imports if len(direct_import)]
# print "require_from_module_default", direct, len(direct)
for i, _import in enumerate(direct[:]):
if " as " in _import:
(packaged_name,local_name) = _import.split(" as ")
if packaged_name.strip() in ["default", "*"]: # this is just the same as var local_name = require('package')
direct[i] = local_name
else:
# print "here!!"
raise ValueError(_import + " in " + __file__)
# suffixed.append((packaged_name,local_name))
else:
# direct[i] = _import == direct[i]
pass
# import *{ named, ... }* from 'package'
if destructured_import_string:
destructured_imports = [x.strip() for x in destructured_import_string.split(",")]
# import { named *as* othername, ... } from 'package'
for _import in destructured_imports:
if " as " in _import:
(packaged_name,local_name) = _import.split(" as ")
if packaged_name.strip() == "default": # this is just the same as var local_name = require('package')
direct.append(local_name)
else:
suffixed.append((packaged_name,local_name))
else:
destructured.append(_import)
if len(direct) == 1:
definition.append("var {definition} = require('{path}')".format(definition=direct[0], path=path))
if len(destructured):
if len(direct) < 1:
imported = "require('{path}')".format(path=path)
else:
imported = direct[0]
definition.append("var {{ {module} }} = {imported}".format(imported = imported, module=", ".join(destructured)))
# e.g. : import { reducer as formReducer } from 'redux-form'
# : var formReducer = require('redux-form').reducer
if len(suffixed):
if len(direct) < 1:
imported = "require('{path}')".format(path=path)
else:
imported = direct[0]
for (packaged_name,local_name) in suffixed:
definition.append("\tvar {local_name} = {imported}.{packedname}".format(local_name=local_name,imported = imported, packedname=packaged_name))
if len(direct) > 1:
raise ValueError("Not really sure how to handle more than 1 top level import: %s" % direct )
return "\n".join(definition)
explored = {}
local_packages = []
def fix_imports(lines, filename):
compiledImportRE = re.compile(importExpression, 0)
for index, line in enumerate(lines):
if "import" in line:
# print line
result = compiledImportRE.match(line)
if result is not None:
# print "line"
data = result.groupdict()
# print "data", data
if data['module'] or data['varname']:
output = require_from_module_default( data['varname'], data['module'] , data['path'])
# print "after data"
# Found a local package
if data['path'].startswith("."):
local_package = path.realpath(path.join(path.dirname(filename), data['path'] ))
if local_package not in explored:
local_packages.append(local_package)
lines[index] = output
# print line.rjust(70, ' '), " ===> ", output
output = ""
#
# with open(filename, 'w') as file:
# file.writelines( ["%s\n" % line for line in lines ] )
def fix_exports(lines, filename):
module_default = None
module_exports = []
exportWord = re.compile(r"export\s+([a-zA-Z]+)\s+", 0)
exportDefault = re.compile(r"export\s+default", 0)
delimit = {
'const' : '=',
'function' : '(',
'class' : '{',
'var' : '='
}
for line_index, line in enumerate(lines):
if "export" in line:
match = exportDefault.match(line)
output = line.replace("export", "").strip()
# print "testing: %s" % line
if match: # DEFAULT
module_default = {'value': output.replace("default", "").split(" = ")[0].strip(), 'line': line_index}
else:
match = exportWord.match(line)
output = line.replace("export", "").strip()
# print "here"
if match: # CONST
# print "exportWord = match"
_type = output.split(" ")[0]
out = output.replace(_type, "").split(delimit[_type])[0].strip()
if _type == "class":
out = out.split(" ")[0]
# print line
# print _type, "---->", out
# print output, line_index
lines[line_index] = output
module_exports.append(
{
"external":out,
"local":out
})
# print lines
if len(module_exports) and module_default:
# print "FAIL", module_exports, module_default
lines[module_default['line']] = "module.exports = {}".format(module_default['value'])
for me in module_exports:
lines.append("module.exports.{external} = {local}".format(external=me['external'], local=me['local']))
# module.exports.notificationReducer = notificationReducer
else:
if module_default:
lines[module_default['line']] = "module.exports = {}".format(module_default['value'])
elif len(module_exports):
lines.append("\nmodule.exports = {")
for me, has_more in lookahead(module_exports):
lines.append(" {external}: {local}".format(external=me['external'], local=me['local']))
if has_more:
lines[-1] = lines[-1] + ","
lines.append("}")
with open(filename, 'w') as file:
file.writelines( ["%s\n" % line for line in lines ] )
# print "PASSING"
def parse(input_filename):
filename = input_filename
if filename in explored:
# (click.style("Circular import to {filename} - ignoring".format(filename=filename), bg='white', fg='red'))
return
possible_extensions = ['.jsx', '.js']
if not path.isfile(filename):
for ext in possible_extensions:
if path.isfile(filename + ext):
filename += ext
if path.isdir(filename):
print "Didn't process:", filename
return
with open(filename) as f:
lines = [line.rstrip('\n') for line in f]
click.echo(click.style('Parsing: ' + filename, bg='blue', fg='white'))
fix_imports(lines, filename)
fix_exports(lines, filename)
if input_filename not in explored:
explored[input_filename] = True
return
# def parse(input_filename):
@click.command()
@click.argument('f', type=click.Path(exists=True))
def six_to_five(f):
"""Doc """
abspath = path.realpath(path.join(path.dirname( __file__ ), f))
parse(abspath)
while len(local_packages) > 0:
parse(local_packages.pop(0))
click.echo(click.style('Total files explored: {}'.format(len(explored.keys())), fg='green'))
if __name__ == '__main__':
six_to_five()
@DanielKoehler
Copy link
Author

Imports

e.g.

import { apiCall, CALL_API } from '../middleware/api'
import * as ActionTypes from '../constants/listing-action-types'

Becomes:

var { apiCall, CALL_API } = require('../middleware/api')
var ActionTypes = require('../constants/listing-action-types')

Exports

e.g.

export function filterByLetter(letter) {
  return { type: ActionTypes.FILTER_BY_LETTER, letter }
}
export function filterByCategory(category) {
  return { type: ActionTypes.FILTER_BY_CATEGORY, category }
}

Becomes

function filterByLetter(letter) {
  return { type: ActionTypes.FILTER_BY_LETTER, letter }
}
function filterByCategory(category) {
  return { type: ActionTypes.FILTER_BY_CATEGORY, category }
}

module.exports = {
    filterByLetter: filterByLetter,
    filterByCategory: filterByCategory
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment