Skip to content

Instantly share code, notes, and snippets.

@foxyblocks
Created August 10, 2016 06:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foxyblocks/af783d1e447bfcfb4b1647a3e056d2d0 to your computer and use it in GitHub Desktop.
Save foxyblocks/af783d1e447bfcfb4b1647a3e056d2d0 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
ROOT_PATH = File.dirname(__FILE__) + '/js/dashboard/'
COMPONENT_REGEX = /window\.(\w+) =.+(React|Reflux)/
files = Dir.glob(ROOT_PATH + "**/*.{js,jsx,coffee,cjsx}")
# hard coded modules that we want to require. This list will be amended by searching for
# `window.X = (React|Redux)` definitions in the code base
components = [
{name: 'UserActions', import_path: 'user_actions'},
{name: 'UrlService', import_path: 'services/url_service'},
{name: 'Reflux', import_path: 'reflux'},
{name: 'PureRenderMixin', import_path: 'react-addons-pure-render-mixin'},
{name: 'ReactDOM', import_path: 'react-dom'},
{name: 'React', import_path: 'react'},
{name: 'classNames', import_path: 'classnames'},
{name: 'InlineSVG', import_path: 'react-inline-svg'},
{name: 'Routes', import_path: 'routes'},
{name: 'HotKeyMixin', import_path: 'mixins/hot_key_mixin'},
{name: 'Analytics', import_path: 'lib/analytics'},
{name: 'Timezone', import_path: 'lib/timezone'},
{name: 'Update', import_path: 'lib/update'},
{name: 'APIRequestMixin', import_path: 'mixins/api_request_mixin'},
{name: 'DocumentEventListenerMixin', import_path: 'mixins/document_event_listener_mixin'},
{name: 'Utils', import_path: 'lib/utils'},
]
def make_import_path(full_path)
full_path.gsub(ROOT_PATH, "").gsub(/\.\w+$/, '')
end
def make_import_statement(name, file_name, component_path)
path = make_import_path(component_path)
if is_coffee?(file_name)
"#{name} = require('#{path}')"
else
"import #{name} from '#{path}';"
end
end
def is_coffee?(file_name)
file_name.match(/\.(coffee|cjsx)/)
end
# for a files contents, finds the place where we should add the next import statment.
# this should be at the top, but after any comment blocks or other require/import statments already
# in the file.
def find_insertion_point(contents, file_name)
if is_coffee?(file_name)
comment = /^\#/
block_comment_start = /\#{3}/
block_comment_end = /\#{3}/
import = /require/
else
comment = /^\/\//
block_comment_start = /\/\*/
block_comment_end = /\*\//
import = /(import|require)/
end
line_number = 0
in_block_comment = false
lines = contents.split("\n")
# step through the lines of the file until we reach the first line that isn't a comment or a
# a require statement.
lines.each_with_index do |line, index|
if in_block_comment
if line.match(block_comment_end)
in_block_comment = false
end
line_number = line_number + 1
next
end
if line.match(block_comment_start)
in_block_comment = true
line_number = line_number + 1
next
end
if line.match(comment) || line.match(import)
line_number = line_number + 1
elsif line.empty? && lines[index + 1].match(import)
line_number = line_number + 1
else
break
end
end
return line_number
end
# iterate over all files and find "window.Foo =" statements
# replace those with explicit exports and add them to the list of components
files.each do |file_name|
text = File.read(file_name)
matches = text.scan(COMPONENT_REGEX)
components += matches.map do |match|
{name: match[0], import_path: file_name}
end
replacement =
if is_coffee?(file_name)
'module.exports = \2'
else
'export default \2'
end
new_contents = text.gsub(COMPONENT_REGEX, replacement)
# To write changes to the file, use:
File.open(file_name, "w") {|file| file.puts new_contents }
end
# Add import/require statements to all the files where global components have been used
files.each do |file_name|
text = File.read(file_name)
new_contents = text
components.each do |component|
# skip if we are in the component file itself
next if make_import_path(file_name) == component[:import_path]
name = component[:name]
# skip if component isn't used in this file
next unless text.match(/(\W#{name}\W|^#{name}|#{name}\.)/)
# skip if we already imported that component
next if new_contents.match(/^(import #{name}|#{name} = require)/)
# find the next insertion point for an import statement
insertion_point = find_insertion_point(new_contents, file_name)
# split into lines
lines = new_contents.split("\n")
if insertion_point == 0
lines.insert(insertion_point, "")
end
# insert new line
lines.insert(insertion_point, make_import_statement(name, file_name, component[:import_path]))
# join everything again
new_contents = lines.join("\n")
end
# write the new contents
File.open(file_name, "w") {|file| file.puts new_contents }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment