import_js 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/import_js/configuration.rb +12 -4
- data/lib/import_js/emacs_editor.rb +154 -0
- data/lib/import_js/import_statement.rb +111 -0
- data/lib/import_js/importer.rb +131 -91
- data/lib/import_js/js_module.rb +18 -4
- data/lib/import_js/vim_editor.rb +8 -0
- data/lib/import_js.rb +8 -2
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b23358bb0d61ef8fac8b2e8789f2bfffa092fb9
|
4
|
+
data.tar.gz: 132643d9f5029de7cf5087d496fedb91e53d903b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51cd0b9b042fe549668137d6635a68589d031a72ab40fae39908604101fa632363b701546c194d87b0a3140714c10234434127fe4bd679f40c8d8d56855a26df
|
7
|
+
data.tar.gz: b7ccd7a213c9dbd9cd80db90b70421dd16e1b4a9d88a79b67b63ea210611ea72f66c805d07c698baf4dae78dd854292012e56b89e07db04f21e9aed627813cfc
|
@@ -8,8 +8,8 @@ module ImportJS
|
|
8
8
|
'aliases' => {},
|
9
9
|
'declaration_keyword' => 'var',
|
10
10
|
'excludes' => [],
|
11
|
-
'jshint_cmd' => 'jshint',
|
12
11
|
'lookup_paths' => ['.'],
|
12
|
+
'strip_file_extensions' => ['.js', '.jsx']
|
13
13
|
}
|
14
14
|
|
15
15
|
# Class that initializes configuration from a .importjs.json file
|
@@ -28,19 +28,27 @@ module ImportJS
|
|
28
28
|
@config[key]
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# @param variable_name [String]
|
32
|
+
# @param path_to_current_file [String?]
|
33
|
+
# @return [ImportJS::JSModule?]
|
34
|
+
def resolve_alias(variable_name, path_to_current_file)
|
32
35
|
path = @config['aliases'][variable_name]
|
33
36
|
return resolve_destructured_alias(variable_name) unless path
|
34
37
|
|
35
38
|
path = path['path'] if path.is_a? Hash
|
36
|
-
|
39
|
+
|
40
|
+
if path_to_current_file && !path_to_current_file.empty?
|
41
|
+
path = path.sub(/\{filename\}/,
|
42
|
+
File.basename(path_to_current_file, '.*'))
|
43
|
+
end
|
44
|
+
ImportJS::JSModule.new(nil, path, [])
|
37
45
|
end
|
38
46
|
|
39
47
|
def resolve_destructured_alias(variable_name)
|
40
48
|
@config['aliases'].each do |_, path|
|
41
49
|
next if path.is_a? String
|
42
50
|
if (path['destructure'] || []).include?(variable_name)
|
43
|
-
js_module = ImportJS::JSModule.new(nil, path['path'],
|
51
|
+
js_module = ImportJS::JSModule.new(nil, path['path'], [])
|
44
52
|
js_module.is_destructured = true
|
45
53
|
return js_module
|
46
54
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ImportJS
|
3
|
+
end
|
4
|
+
|
5
|
+
class ImportJS::EmacsEditor
|
6
|
+
attr_accessor :current_word
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
loop do
|
10
|
+
input = gets.chomp
|
11
|
+
command, value, path = input.split(':')
|
12
|
+
|
13
|
+
begin
|
14
|
+
@path = path
|
15
|
+
@file = File.readlines(path).map(&:chomp)
|
16
|
+
@current_word = value
|
17
|
+
|
18
|
+
case command
|
19
|
+
when 'import'
|
20
|
+
ImportJS::Importer.new(self).import
|
21
|
+
write_file
|
22
|
+
puts 'import:success'
|
23
|
+
when 'goto'
|
24
|
+
ImportJS::Importer.new(self).goto
|
25
|
+
end
|
26
|
+
rescue Exception => e
|
27
|
+
puts e.inspect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_file
|
33
|
+
new_file = File.open(@path, 'w')
|
34
|
+
@file.each {|line| new_file.puts(line) }
|
35
|
+
new_file.close
|
36
|
+
end
|
37
|
+
|
38
|
+
# Open a file specified by a path.
|
39
|
+
#
|
40
|
+
# @param file_path [String]
|
41
|
+
def open_file(file_path)
|
42
|
+
puts "goto:success:#{File.expand_path(file_path)}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the path to the file currently being edited. May return `nil` if an
|
46
|
+
# anonymous file is being edited.
|
47
|
+
#
|
48
|
+
# @return [String?]
|
49
|
+
def path_to_current_file
|
50
|
+
# Not yet implemented for Emacs
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Display a message to the user.
|
55
|
+
#
|
56
|
+
# @param str [String]
|
57
|
+
def message(str)
|
58
|
+
puts str
|
59
|
+
end
|
60
|
+
|
61
|
+
# Read the entire file into a string.
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
def current_file_content
|
65
|
+
@file.join('\n')
|
66
|
+
end
|
67
|
+
|
68
|
+
# Reads a line from the file.
|
69
|
+
#
|
70
|
+
# Lines are one-indexed, so 1 means the first line in the file.
|
71
|
+
# @return [String]
|
72
|
+
def read_line(line_number)
|
73
|
+
@file[line_number - 1]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get the cursor position.
|
77
|
+
#
|
78
|
+
# @return [Array(Number, Number)]
|
79
|
+
def cursor
|
80
|
+
return 0, 0
|
81
|
+
end
|
82
|
+
|
83
|
+
# Place the cursor at a specified position.
|
84
|
+
#
|
85
|
+
# @param position_tuple [Array(Number, Number)] the row and column to place
|
86
|
+
# the cursor at.
|
87
|
+
def cursor=(position_tuple)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Delete a line.
|
91
|
+
#
|
92
|
+
# @param line_number [Number] One-indexed line number.
|
93
|
+
# 1 is the first line in the file.
|
94
|
+
def delete_line(line_number)
|
95
|
+
@file.delete_at(line_number - 1)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Append a line right after the specified line.
|
99
|
+
#
|
100
|
+
# Lines are one-indexed, but you need to support appending to line 0 (add
|
101
|
+
# content at top of file).
|
102
|
+
# @param line_number [Number]
|
103
|
+
def append_line(line_number, str)
|
104
|
+
@file.insert(line_number, str)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Count the number of lines in the file.
|
108
|
+
#
|
109
|
+
# @return [Number] the number of lines in the file
|
110
|
+
def count_lines
|
111
|
+
@file.size
|
112
|
+
end
|
113
|
+
|
114
|
+
# Ask the user to select something from a list of alternatives.
|
115
|
+
#
|
116
|
+
# @param heading [String] A heading text
|
117
|
+
# @param alternatives [Array<String>] A list of alternatives
|
118
|
+
# @return [Number, nil] the index of the selected alternative, or nil if
|
119
|
+
# nothing was selected.
|
120
|
+
def ask_for_selection(heading, alternatives)
|
121
|
+
puts "asking for selection"
|
122
|
+
puts heading
|
123
|
+
puts JSON.pretty_generate(alternatives)
|
124
|
+
return
|
125
|
+
|
126
|
+
# need to implement this
|
127
|
+
escaped_list = [heading]
|
128
|
+
escaped_list << alternatives.each_with_index.map do |alternative, i|
|
129
|
+
"\"#{i + 1}: #{alternative}\""
|
130
|
+
end
|
131
|
+
escaped_list_string = '[' + escaped_list.join(',') + ']'
|
132
|
+
|
133
|
+
selected_index = VIM.evaluate("inputlist(#{escaped_list_string})")
|
134
|
+
return if selected_index < 1
|
135
|
+
selected_index - 1
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get the preferred max length of a line
|
139
|
+
# @return [Number?]
|
140
|
+
def max_line_length
|
141
|
+
80
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [String] shiftwidth number of spaces if expandtab is not set,
|
145
|
+
# otherwise `\t`
|
146
|
+
def tab
|
147
|
+
return ' ' * shift_width || 2
|
148
|
+
end
|
149
|
+
|
150
|
+
# @return [Number?]
|
151
|
+
def shift_width
|
152
|
+
2
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module ImportJS
|
2
|
+
# Class that represents an import statement, e.g.
|
3
|
+
# "const foo = require('foo');"
|
4
|
+
class ImportStatement
|
5
|
+
REGEX_CONST_LET_VAR = %r{
|
6
|
+
\A
|
7
|
+
(?:const|let|var)\s+ # declaration keyword
|
8
|
+
(?<assignment>.+?) # <assignment> variable assignment
|
9
|
+
\s*=\s*
|
10
|
+
require\(
|
11
|
+
(?<quote>'|") # <quote> opening quote
|
12
|
+
(?<path>[^\2]+) # <path> module path
|
13
|
+
\k<quote> # closing quote
|
14
|
+
\);?
|
15
|
+
\s*
|
16
|
+
}xm
|
17
|
+
|
18
|
+
REGEX_IMPORT = %r{
|
19
|
+
\A
|
20
|
+
import\s+
|
21
|
+
(?<assignment>.*?) # <assignment> variable assignment
|
22
|
+
\s+from\s+
|
23
|
+
(?<quote>'|") # <quote> opening quote
|
24
|
+
(?<path>[^\2]+) # <path> module path
|
25
|
+
\k<quote> # closing quote
|
26
|
+
;?\s*
|
27
|
+
}xm
|
28
|
+
|
29
|
+
attr_accessor :assignment
|
30
|
+
attr_accessor :original_import_string # a cache of the parsed import string
|
31
|
+
attr_accessor :variables
|
32
|
+
attr_accessor :is_destructured # can't use `destructured?` because of 1.9.3
|
33
|
+
attr_accessor :path
|
34
|
+
|
35
|
+
# @param string [String] a possible import statement, e.g.
|
36
|
+
# `const foo = require('foo');`
|
37
|
+
# @return [ImportJS::ImportStatement?] a parsed statement, or nil if the
|
38
|
+
# string can't be parsed
|
39
|
+
def self.parse(string)
|
40
|
+
match = REGEX_CONST_LET_VAR.match(string) ||
|
41
|
+
REGEX_IMPORT.match(string)
|
42
|
+
return unless match
|
43
|
+
|
44
|
+
statement = new
|
45
|
+
statement.original_import_string = match.string
|
46
|
+
statement.path = match[:path]
|
47
|
+
statement.assignment = match[:assignment]
|
48
|
+
if dest_match = statement.assignment.match(/\{\s*(.*)\s*\}/)
|
49
|
+
statement.variables = dest_match[1].split(/,\s*/).map(&:strip)
|
50
|
+
statement.is_destructured = true
|
51
|
+
else
|
52
|
+
statement.variables = [statement.assignment]
|
53
|
+
end
|
54
|
+
statement
|
55
|
+
end
|
56
|
+
|
57
|
+
# Injects a new variable into an already existing set of destructured
|
58
|
+
# variables.
|
59
|
+
# @param variable_name [String]
|
60
|
+
def inject_variable(variable_name)
|
61
|
+
variables << variable_name
|
62
|
+
variables.sort!.uniq!
|
63
|
+
|
64
|
+
@original_import_string = nil # clear import string cache if there was one
|
65
|
+
end
|
66
|
+
|
67
|
+
# Deletes a variable from an already existing set of destructured
|
68
|
+
# variables.
|
69
|
+
# @param variable_name [String]
|
70
|
+
def delete_variable(variable_name)
|
71
|
+
variables.delete(variable_name)
|
72
|
+
|
73
|
+
@original_import_string = nil # clear import string cache if there was one
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Array] an array that can be used in `uniq!` to dedupe equal
|
77
|
+
# statements, e.g.
|
78
|
+
# `const foo = require('foo');`
|
79
|
+
# `import foo from 'foo';`
|
80
|
+
def normalize
|
81
|
+
[variables, path]
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param declaration_keyword [String] const, let, var, or import
|
85
|
+
# @param max_line_length [Number] where to cap lines at
|
86
|
+
# @param tab [String] e.g. ' ' (two spaces)
|
87
|
+
# @return a generated import statement string
|
88
|
+
def to_import_string(declaration_keyword, max_line_length, tab)
|
89
|
+
return original_import_string if original_import_string
|
90
|
+
|
91
|
+
declaration = if is_destructured
|
92
|
+
"#{declaration_keyword} { #{variables.join(', ')} }"
|
93
|
+
else
|
94
|
+
"#{declaration_keyword} #{variables.first}"
|
95
|
+
end
|
96
|
+
|
97
|
+
equals, value = if declaration_keyword == 'import'
|
98
|
+
['from', "'#{path}';"]
|
99
|
+
else
|
100
|
+
['=', "require('#{path}');"]
|
101
|
+
end
|
102
|
+
|
103
|
+
if max_line_length &&
|
104
|
+
"#{declaration} #{equals} #{value}".length > max_line_length
|
105
|
+
"#{declaration} #{equals}\n#{tab}#{value}"
|
106
|
+
else
|
107
|
+
"#{declaration} #{equals} #{value}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/import_js/importer.rb
CHANGED
@@ -23,8 +23,16 @@ module ImportJS
|
|
23
23
|
current_row, current_col = @editor.cursor
|
24
24
|
|
25
25
|
old_buffer_lines = @editor.count_lines
|
26
|
-
|
27
|
-
return unless
|
26
|
+
js_module = find_one_js_module(variable_name)
|
27
|
+
return unless js_module
|
28
|
+
|
29
|
+
old_imports = find_current_imports
|
30
|
+
inject_js_module(variable_name, js_module, old_imports[:imports])
|
31
|
+
replace_imports(old_imports[:newline_count],
|
32
|
+
old_imports[:imports],
|
33
|
+
old_imports[:imports_start_at])
|
34
|
+
lines_changed = @editor.count_lines - old_buffer_lines
|
35
|
+
return unless lines_changed
|
28
36
|
@editor.cursor = [current_row + lines_changed, current_col]
|
29
37
|
end
|
30
38
|
|
@@ -39,103 +47,148 @@ module ImportJS
|
|
39
47
|
@editor.open_file(js_module.file_path)
|
40
48
|
end
|
41
49
|
|
50
|
+
def fix_imports
|
51
|
+
remove_unused_imports
|
52
|
+
import_all
|
53
|
+
end
|
54
|
+
|
42
55
|
# Finds all variables that haven't yet been imported.
|
43
56
|
def import_all
|
44
57
|
@config.refresh
|
45
|
-
|
58
|
+
undefined_variables = run_eslint_command.map do |line|
|
59
|
+
/"([^"]+)" is not defined/.match(line) do |match_data|
|
60
|
+
match_data[1]
|
61
|
+
end
|
62
|
+
end.compact.uniq
|
46
63
|
|
47
|
-
if
|
48
|
-
|
49
|
-
|
64
|
+
return message('No variables to import') if undefined_variables.empty?
|
65
|
+
|
66
|
+
old_imports = find_current_imports
|
67
|
+
undefined_variables.each do |variable|
|
68
|
+
if js_module = find_one_js_module(variable)
|
69
|
+
inject_js_module(variable, js_module, old_imports[:imports])
|
70
|
+
end
|
50
71
|
end
|
72
|
+
replace_imports(old_imports[:newline_count],
|
73
|
+
old_imports[:imports],
|
74
|
+
old_imports[:imports_start_at])
|
75
|
+
end
|
76
|
+
|
77
|
+
def remove_unused_imports
|
78
|
+
@config.refresh
|
79
|
+
unused_variables = run_eslint_command.map do |line|
|
80
|
+
/"([^"]+)" is defined but never used/.match(line) do |match_data|
|
81
|
+
match_data[1]
|
82
|
+
end
|
83
|
+
end.compact.uniq
|
51
84
|
|
52
|
-
|
53
|
-
|
85
|
+
old_imports = find_current_imports
|
86
|
+
new_imports = old_imports[:imports].reject do |import_statement|
|
87
|
+
unused_variables.each do |unused_variable|
|
88
|
+
import_statement.delete_variable(unused_variable)
|
89
|
+
end
|
90
|
+
import_statement.variables.empty?
|
54
91
|
end
|
92
|
+
replace_imports(old_imports[:newline_count],
|
93
|
+
new_imports,
|
94
|
+
old_imports[:imports_start_at])
|
55
95
|
end
|
56
96
|
|
57
97
|
private
|
58
98
|
|
59
99
|
def message(str)
|
60
|
-
@editor.message("
|
100
|
+
@editor.message("ImportJS: #{str}")
|
61
101
|
end
|
62
102
|
|
63
|
-
# @return [Array]
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
103
|
+
# @return [Array<String>] the output from eslint, line by line
|
104
|
+
def run_eslint_command
|
105
|
+
command = %w[
|
106
|
+
eslint
|
107
|
+
--stdin
|
108
|
+
--format compact
|
109
|
+
--rule 'no-undef: 2'
|
110
|
+
--rule 'no-unused-vars: [2, { "vars": "all", "args": "none" }]'
|
111
|
+
].join(' ')
|
112
|
+
out, err = Open3.capture3(command,
|
113
|
+
stdin_data: @editor.current_file_content)
|
114
|
+
|
115
|
+
if out =~ /Error - Parsing error: / ||
|
116
|
+
out =~ /Unrecoverable syntax error/
|
117
|
+
fail ImportJS::ParseError.new, out
|
118
|
+
end
|
119
|
+
|
120
|
+
if err =~ /SyntaxError: / ||
|
121
|
+
err =~ /eslint: command not found/
|
122
|
+
fail ImportJS::ParseError.new, err
|
75
123
|
end
|
76
|
-
|
124
|
+
|
125
|
+
out.split("\n")
|
77
126
|
end
|
78
127
|
|
79
128
|
# @param variable_name [String]
|
80
|
-
|
129
|
+
# @return [ImportJS::JSModule?]
|
130
|
+
def find_one_js_module(variable_name)
|
81
131
|
@timing = { start: Time.now }
|
82
132
|
js_modules = find_js_modules(variable_name)
|
83
133
|
@timing[:end] = Time.now
|
84
134
|
if js_modules.empty?
|
85
|
-
message(
|
86
|
-
No
|
87
|
-
EOS
|
135
|
+
message(
|
136
|
+
"No JS module to import for variable `#{variable_name}` #{timing}")
|
88
137
|
return
|
89
138
|
end
|
90
139
|
|
91
|
-
|
92
|
-
return unless resolved_js_module
|
93
|
-
|
94
|
-
write_imports(variable_name, resolved_js_module)
|
140
|
+
resolve_one_js_module(js_modules, variable_name)
|
95
141
|
end
|
96
142
|
|
97
143
|
# @param variable_name [String]
|
98
144
|
# @param js_module [ImportJS::JSModule]
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
# Ensure that there is a blank line after the block of all imports
|
103
|
-
unless @editor.read_line(old_imports[:newline_count] + 1).strip.empty?
|
104
|
-
@editor.append_line(old_imports[:newline_count], '')
|
105
|
-
end
|
106
|
-
|
107
|
-
modified_imports = old_imports[:imports] # Array
|
108
|
-
|
145
|
+
# @param imports [Array<ImportJS::ImportStatement>]
|
146
|
+
def inject_js_module(variable_name, js_module, imports)
|
109
147
|
# Add new import to the block of imports, wrapping at the max line length
|
110
148
|
unless js_module.is_destructured && inject_destructured_variable(
|
111
|
-
variable_name, js_module,
|
112
|
-
|
149
|
+
variable_name, js_module, imports)
|
150
|
+
imports.unshift(js_module.to_import_statement(variable_name))
|
113
151
|
end
|
114
152
|
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
153
|
+
# Remove duplicate import statements
|
154
|
+
imports.uniq!(&:normalize)
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param old_imports_lines [Number]
|
158
|
+
# @param new_imports [Array<ImportJS::ImportStatement>]
|
159
|
+
# @param imports_start_at [Number]
|
160
|
+
def replace_imports(old_imports_lines, new_imports, imports_start_at)
|
161
|
+
# Ensure that there is a blank line after the block of all imports
|
162
|
+
if old_imports_lines + new_imports.length > 0 &&
|
163
|
+
!@editor.read_line(old_imports_lines + imports_start_at + 1).strip.empty?
|
164
|
+
@editor.append_line(old_imports_lines + imports_start_at, '')
|
121
165
|
end
|
122
166
|
|
167
|
+
# Generate import strings
|
168
|
+
import_strings = new_imports.map do |import|
|
169
|
+
import.to_import_string(
|
170
|
+
@config.get('declaration_keyword'),
|
171
|
+
@editor.max_line_length,
|
172
|
+
@editor.tab)
|
173
|
+
end.sort
|
174
|
+
|
123
175
|
# Delete old imports, then add the modified list back in.
|
124
|
-
|
125
|
-
|
176
|
+
old_imports_lines.times { @editor.delete_line(1 + imports_start_at) }
|
177
|
+
import_strings.reverse_each do |import_string|
|
126
178
|
# We need to add each line individually because the Vim buffer will
|
127
179
|
# convert newline characters to `~@`.
|
128
|
-
|
180
|
+
import_string.split("\n").reverse_each do |line|
|
181
|
+
@editor.append_line(0 + imports_start_at, line)
|
182
|
+
end
|
129
183
|
end
|
130
184
|
end
|
131
185
|
|
132
186
|
def inject_destructured_variable(variable_name, js_module, imports)
|
133
187
|
imports.each do |import|
|
134
|
-
|
135
|
-
next unless
|
188
|
+
next unless import.path == js_module.import_path
|
189
|
+
next unless import.is_destructured
|
136
190
|
|
137
|
-
|
138
|
-
import.sub!(/.*/, "#{match[1]}#{variables.join(', ')}#{match[4]}")
|
191
|
+
import.inject_variable(variable_name)
|
139
192
|
return true
|
140
193
|
end
|
141
194
|
false
|
@@ -150,52 +203,38 @@ module ImportJS
|
|
150
203
|
potential_import_lines << line
|
151
204
|
end
|
152
205
|
|
206
|
+
result = {
|
207
|
+
imports: [],
|
208
|
+
newline_count: 0,
|
209
|
+
imports_start_at: 0
|
210
|
+
}
|
211
|
+
|
212
|
+
if potential_import_lines[0] =~ /(['"])use strict\1;?/
|
213
|
+
result[:imports_start_at] = 1
|
214
|
+
potential_import_lines.shift
|
215
|
+
end
|
216
|
+
|
153
217
|
# We need to put the potential imports back into a blob in order to scan
|
154
218
|
# for multiline imports
|
155
219
|
potential_imports_blob = potential_import_lines.join("\n")
|
156
220
|
|
157
|
-
imports = []
|
158
|
-
|
159
221
|
# Scan potential imports for everything ending in a semicolon, then
|
160
222
|
# iterate through those and stop at anything that's not an import.
|
161
223
|
potential_imports_blob.scan(/^.*?;/m).each do |potential_import|
|
162
|
-
|
163
|
-
|
164
|
-
imports << potential_import
|
165
|
-
end
|
166
|
-
|
167
|
-
newline_count = imports.length + imports.reduce(0) do |sum, import|
|
168
|
-
sum + import.scan(/\n/).length
|
169
|
-
end
|
170
|
-
{
|
171
|
-
imports: imports,
|
172
|
-
newline_count: newline_count
|
173
|
-
}
|
174
|
-
end
|
175
|
-
|
176
|
-
# @param variable_name [String]
|
177
|
-
# @param js_module [ImportJS::JSModule]
|
178
|
-
# @return [String] the import string to be added to the imports block
|
179
|
-
def generate_import(variable_name, js_module)
|
180
|
-
declaration_keyword = @config.get('declaration_keyword')
|
181
|
-
if js_module.is_destructured
|
182
|
-
declaration = "#{declaration_keyword} { #{variable_name} } ="
|
183
|
-
else
|
184
|
-
declaration = "#{declaration_keyword} #{variable_name} ="
|
185
|
-
end
|
186
|
-
value = "require('#{js_module.import_path}');"
|
224
|
+
import_statement = ImportJS::ImportStatement.parse(potential_import)
|
225
|
+
break unless import_statement
|
187
226
|
|
188
|
-
|
189
|
-
|
190
|
-
else
|
191
|
-
"#{declaration} #{value}"
|
227
|
+
result[:imports] << import_statement
|
228
|
+
result[:newline_count] += potential_import.scan(/\n/).length + 1
|
192
229
|
end
|
230
|
+
result
|
193
231
|
end
|
194
232
|
|
195
233
|
# @param variable_name [String]
|
196
234
|
# @return [Array]
|
197
235
|
def find_js_modules(variable_name)
|
198
|
-
if alias_module = @config.resolve_alias(variable_name
|
236
|
+
if alias_module = @config.resolve_alias(variable_name,
|
237
|
+
@editor.path_to_current_file)
|
199
238
|
return [alias_module]
|
200
239
|
end
|
201
240
|
|
@@ -210,7 +249,8 @@ module ImportJS
|
|
210
249
|
next if @config.get('excludes').any? do |glob_pattern|
|
211
250
|
File.fnmatch(glob_pattern, f)
|
212
251
|
end
|
213
|
-
js_module = ImportJS::JSModule.new(
|
252
|
+
js_module = ImportJS::JSModule.new(
|
253
|
+
lookup_path, f, @config.get('strip_file_extensions'))
|
214
254
|
next if js_module.skip
|
215
255
|
js_module
|
216
256
|
end.compact
|
@@ -221,7 +261,7 @@ module ImportJS
|
|
221
261
|
@config.package_dependencies.each do |dep|
|
222
262
|
next unless dep =~ /^#{formatted_to_regex(variable_name)}$/
|
223
263
|
js_module = ImportJS::JSModule.new(
|
224
|
-
'node_modules', "node_modules/#{dep}/package.json",
|
264
|
+
'node_modules', "node_modules/#{dep}/package.json", [])
|
225
265
|
next if js_module.skip
|
226
266
|
matched_modules << js_module
|
227
267
|
end
|
@@ -248,8 +288,8 @@ module ImportJS
|
|
248
288
|
end
|
249
289
|
|
250
290
|
selected_index = @editor.ask_for_selection(
|
251
|
-
"\"
|
252
|
-
js_modules.map
|
291
|
+
"\"ImportJS: Pick JS module to import for '#{variable_name}': #{timing}\"",
|
292
|
+
js_modules.map(&:display_name)
|
253
293
|
)
|
254
294
|
return unless selected_index
|
255
295
|
js_modules[selected_index]
|
data/lib/import_js/js_module.rb
CHANGED
@@ -11,8 +11,9 @@ module ImportJS
|
|
11
11
|
# @param lookup_path [String] the lookup path in which this module was found
|
12
12
|
# @param relative_file_path [String] a full path to the file, relative to
|
13
13
|
# the project root.
|
14
|
-
# @param
|
15
|
-
|
14
|
+
# @param strip_file_extensions [Array] a list of file extensions to strip,
|
15
|
+
# e.g. ['.js', '.jsx']
|
16
|
+
def initialize(lookup_path, relative_file_path, strip_file_extensions)
|
16
17
|
@lookup_path = lookup_path
|
17
18
|
@file_path = relative_file_path
|
18
19
|
if relative_file_path.end_with? '/package.json'
|
@@ -26,8 +27,11 @@ module ImportJS
|
|
26
27
|
@import_path = match[1]
|
27
28
|
else
|
28
29
|
@import_path = relative_file_path
|
29
|
-
|
30
|
-
|
30
|
+
strip_file_extensions.each do |ext|
|
31
|
+
if @import_path.end_with?(ext)
|
32
|
+
@import_path = @import_path[0...-ext.length]
|
33
|
+
break
|
34
|
+
end
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
@@ -42,5 +46,15 @@ module ImportJS
|
|
42
46
|
parts << " (main: #{@main_file})" if @main_file
|
43
47
|
parts.join('')
|
44
48
|
end
|
49
|
+
|
50
|
+
# @param variable_name [String]
|
51
|
+
# @return [ImportJS::ImportStatement]
|
52
|
+
def to_import_statement(variable_name)
|
53
|
+
ImportJS::ImportStatement.new.tap do |statement|
|
54
|
+
statement.is_destructured = is_destructured
|
55
|
+
statement.variables = [variable_name]
|
56
|
+
statement.path = import_path
|
57
|
+
end
|
58
|
+
end
|
45
59
|
end
|
46
60
|
end
|
data/lib/import_js/vim_editor.rb
CHANGED
@@ -10,6 +10,14 @@ module ImportJS
|
|
10
10
|
VIM.evaluate("expand('<cword>')")
|
11
11
|
end
|
12
12
|
|
13
|
+
# Get the path to the file currently being edited. May return `nil` if an
|
14
|
+
# anonymous file is being edited.
|
15
|
+
#
|
16
|
+
# @return [String?]
|
17
|
+
def path_to_current_file
|
18
|
+
VIM.evaluate("expand('%')")
|
19
|
+
end
|
20
|
+
|
13
21
|
# Open a file specified by a path.
|
14
22
|
#
|
15
23
|
# @param file_path [String]
|
data/lib/import_js.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# Namespace declaration
|
2
2
|
module ImportJS
|
3
|
-
# We initialize
|
4
|
-
#
|
3
|
+
# We initialize the "ImportJS" namespace here so that we can define classes
|
4
|
+
# under that namespace, e.g. `ImportJS::Importer`.
|
5
|
+
|
6
|
+
class ParseError < StandardError
|
7
|
+
# Error thrown when a JS file can't be parsed
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
require_relative 'import_js/js_module'
|
12
|
+
require_relative 'import_js/import_statement'
|
8
13
|
require_relative 'import_js/importer'
|
9
14
|
require_relative 'import_js/vim_editor'
|
15
|
+
require_relative 'import_js/emacs_editor'
|
10
16
|
require_relative 'import_js/command_line_editor'
|
11
17
|
require_relative 'import_js/configuration'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: import_js
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henric Trotzig
|
@@ -21,6 +21,8 @@ files:
|
|
21
21
|
- lib/import_js.rb
|
22
22
|
- lib/import_js/command_line_editor.rb
|
23
23
|
- lib/import_js/configuration.rb
|
24
|
+
- lib/import_js/emacs_editor.rb
|
25
|
+
- lib/import_js/import_statement.rb
|
24
26
|
- lib/import_js/importer.rb
|
25
27
|
- lib/import_js/js_module.rb
|
26
28
|
- lib/import_js/vim_editor.rb
|