|
require 'pathname' |
|
|
|
glob_pattern = ARGV.first |
|
|
|
def migrate(target_path) |
|
target = File.read(target_path) |
|
target_template = target.match(%r|<template.+/template>|m).to_s |
|
target_script = target.match(%r|<script.+/script>|m).to_s |
|
|
|
return if target_script.empty? |
|
|
|
data = target_script.match(/data \(\):?.+?\n }/m) |
|
data_defs = [] |
|
if data |
|
data_defs = data.to_s |
|
.gsub("\n", '') |
|
.match(/return ?{\s*(.+?)\s*?}/)[1] |
|
.split(',') |
|
.map { |d| d.split(':').map(&:strip) } |
|
end |
|
|
|
data_types = target_script.match(/type Data = {.+?\n};/m).to_s |
|
data_types = data_types.scan(/^ (\w+?): (.+?);$/).to_h |
|
|
|
props = target_script.match(/props: {.+?\n }/m) |
|
props_names = props ? props.to_s.scan(/ (\w+): {/).flatten : [] |
|
referenced_prop_names = props_names.select { |pn| target_script.scan(/this\.#{pn}(?!\w)/).size > 0 } |
|
|
|
computed = target_script.match(/computed: {.+?\n }/m) |
|
computed_defs = [] |
|
if computed |
|
cnames = computed.to_s.scan(/^ (\w+):? ?\(.*?\)/).flatten |
|
|
|
computed_defs = cnames.map do |name| |
|
oneliner = computed.to_s.match?(/#{name}: \(.*?\)/) |
|
|
|
if oneliner |
|
{ |
|
name: name, |
|
oneliner: true, |
|
code: computed.to_s.match(/^ #{name}: \(.*?\).+$/).to_s |
|
} |
|
else |
|
{ |
|
name: name, |
|
oneliner: false, |
|
code: computed.to_s.match(/ #{name} \(.*?\).+?\n },?\n/m).to_s |
|
} |
|
end |
|
end |
|
end |
|
|
|
meths = target_script.match(/methods: {.+?\n }/m) |
|
meths_defs = [] |
|
if meths |
|
mnames = meths.to_s.scan(/^ (?:async )?(\w+):? ?\(.*?\)/).flatten |
|
|
|
meths_defs = mnames.map do |name| |
|
oneliner = meths.to_s.match?(/#{name}: \(.*?\)/) |
|
|
|
if oneliner |
|
{ |
|
name: name, |
|
oneliner: true, |
|
code:meths.to_s.match(/^ (async )?#{name}: \(.*?\).+$/).to_s |
|
} |
|
else |
|
{ |
|
name: name, |
|
oneliner: false, |
|
code: meths.to_s.match(/ (async )?#{name} \(.*?\).+?\n },?\n/m).to_s |
|
} |
|
end |
|
end |
|
end |
|
|
|
# build setup |
|
setup_args = [].tap do |args| |
|
args << (referenced_prop_names.empty? ? '_' : 'props') |
|
args << '{ emit }' if target_script.include?('this.$emit(') |
|
end |
|
setup_args.clear if setup_args == ['_'] |
|
|
|
setup = [].tap do |s| |
|
s << " setup (#{setup_args.join(', ')}) {" |
|
|
|
if referenced_prop_names.any? |
|
s << "\n" |
|
s << " const { #{referenced_prop_names.join(', ')} } = toRefs(props);\n" |
|
end |
|
|
|
returns = [] |
|
|
|
s << "\n" if data_defs.any? |
|
data_defs.each do |name, default| |
|
returns << name |
|
type = data_types[name] |
|
type = "<#{type}>" if type |
|
s << " const #{name} = ref#{type}(#{default});\n" |
|
end |
|
|
|
s << "\n" if computed_defs.any? |
|
computed_defs.each do |computed_def| |
|
returns << computed_def[:name] |
|
|
|
if computed_def[:oneliner] |
|
computed_def[:code].sub!(/^ (\w+): ?\((.*?)\)(\: .+?)? => (.+?),?$/, " const \\1 = computed((\\2)\\3 => \\4);\n") |
|
else |
|
computed_def[:code].sub!(/^ (\w+) ?\((.*?)\)(\: .+?)? {\n/, " const \\1 = computed((\\2)\\3 => {\n") |
|
computed_def[:code].sub!(/^ },?$/, ' });') |
|
end |
|
s << computed_def[:code] |
|
end |
|
|
|
s << "\n" if meths_defs.any? |
|
meths_defs.each do |meths_def| |
|
returns << meths_def[:name] |
|
|
|
if meths_def[:oneliner] |
|
meths_def[:code].sub!(/^ (async )?(\w+): ?\((.*?)\)(\: .+?)? => (.+?),?$/, " const \\2 = \\1(\\3)\\4 => \\5;\n") |
|
else |
|
meths_def[:code].sub!(/^ (async )?(\w+) ?\((.*?)\)(\: \w+)? {\n/, " const \\2 = \\1(\\3)\\4 => {\n") |
|
meths_def[:code].sub!(/^ },?$/, ' };') |
|
end |
|
s << meths_def[:code] |
|
end |
|
|
|
returns.select! { |name| target_template.match?(/(?<!\w)#{name}(?!\w)/) } |
|
|
|
if returns.any? |
|
s << "\n" |
|
s << " return {\n" |
|
s << "#{returns.map.with_index { |n, i| " #{n}#{',' unless i == returns.size - 1}" }.join("\n")}\n" |
|
s << " };\n" |
|
end |
|
|
|
s << ' }' |
|
end |
|
|
|
# setup |
|
target_script.sub!(/^}\);/, "#{setup.join}\n});") if data_defs.any? || computed_defs.any? || meths_defs.any? |
|
|
|
# convert code to new syntax and new package |
|
packages = [].tap do |pk| |
|
pk << 'computed' if computed_defs.any? |
|
pk << 'defineComponent' |
|
pk << 'nextTick' if target_script.include?('this.$nextTick(') |
|
pk << 'ref' if data_defs.any? |
|
pk << 'toRefs' if referenced_prop_names.any? |
|
end |
|
|
|
target_script.sub!(/(<script .+>\n)/, "\\1import { #{packages.join(', ')} } from '@vue/composition-api';\n#{ "\n" if target_script.count('^import ').zero? }") |
|
target_script.sub!('Vue.extend', 'defineComponent') |
|
|
|
referenced_prop_names.each do |name| |
|
target_script.gsub!(/this\.#{name}(?!\w)/, "#{name}.value") |
|
end |
|
|
|
data_defs.each do |name, _| |
|
target_script.gsub!(/this\.#{name}(?!\w)/, "#{name}.value") |
|
end |
|
|
|
props_names.each do |name| |
|
target_script.gsub!(/this.#{name}(?!\w)/, name) |
|
end |
|
|
|
computed_defs.each do |computed_def| |
|
name = computed_def[:name] |
|
target_script.gsub!(/this.#{name}(?!\w)/, "#{name}.value") |
|
end |
|
|
|
meths_defs.each do |meths_def| |
|
name = meths_def[:name] |
|
target_script.gsub!(/this.#{name}(?!\w)/, name) |
|
end |
|
|
|
target_script.gsub!(/this.\$emit\(/, 'emit(') |
|
target_script.gsub!(/this.\$nextTick\(/, 'nextTick(') |
|
|
|
target_script.gsub!(/type: (.+?) as PropType<(.+)>/, 'type: \1 as () => \2') |
|
target_script.gsub!(/type: Function as/, 'type: (Function as unknown) as') |
|
|
|
# cleanup leagacy code |
|
target_script.sub!(/^ name:.+\n/, '') |
|
target_script.sub!(/^ data \(\):?.+? },?\n/m, '') |
|
target_script.sub!(/^ computed: {.+?\n },?\n/m, '') |
|
target_script.sub!(/^ methods: {.+?\n },?\n/m, '') |
|
target_script.sub!(/^import .+ from 'vue';\n/, '') |
|
target_script.sub!(/^type Data = {.+?\n};\n\n?/m, '') |
|
|
|
File.write(target_path, target.sub(%r|<script.+/script>|m, target_script)) |
|
end |
|
|
|
Pathname.pwd.glob(glob_pattern) do |path| |
|
print path.relative_path_from(Pathname.pwd).to_s |
|
migrate(path.to_s) |
|
print ' ...ok' |
|
ensure |
|
puts '' |
|
end |