diff --git a/spec/compilers2/defer_2 b/spec/compilers2/defer_2 new file mode 100644 index 000000000..6c22dcc1b --- /dev/null +++ b/spec/compilers2/defer_2 @@ -0,0 +1,53 @@ +module Data { + const ITEMS = defer [ defer ITEM_1 ] + const ITEM_1 = "Hello" +} + +component Main { + fun componentDidMount : Promise(String) { + let [item] = await Data.ITEMS or return "" + await item + } + + fun render : Html { +
""
+ } +} +-------------------------------------------------------------------------------- +---=== /index.js ===--- +import { + patternVariable as E, + createElement as F, + destructure as C, + useEffect as B, + load as D +} from "runtime"; + +export const + a = `/__mint__/d5f8d9aa357303a4ba78b554c55d598bd238f679b.js`, + A = () => { + B(() => { + (async () => { + const b = C(await D(a), [E]); + if (b === false) { + return `` + }; + const [c] = b; + return await D(c) + })() + }, []); + return F(`div`, {}, [``]) + }; + +---=== /__mint__/d96571c631d11ed793b887a131e80107a65e05223.js ===--- +export const + a = `Hello`, + b = a; + +export default b; + +---=== /__mint__/d5f8d9aa357303a4ba78b554c55d598bd238f679b.js ===--- +export const a = [`/__mint__/d96571c631d11ed793b887a131e80107a65e05223.js`]; + +export default a; + diff --git a/spec/compilers2/here_doc_markdown_escape b/spec/compilers2/here_doc_markdown_escape index f50dc7715..3cf29eb3a 100644 --- a/spec/compilers2/here_doc_markdown_escape +++ b/spec/compilers2/here_doc_markdown_escape @@ -1,7 +1,10 @@ component Main { fun render : Html { <<#MARKDOWN(highlight) + \#{name} ```mint + `Something` + "\#{name}" "First line" \ "Second line" \ "Third line" @@ -16,35 +19,59 @@ import { } from "runtime"; export const A = () => { - return B(C, {}, [B('pre', {}, [B('code', { - class: "language-mint" - }, [ - B('span', { - className: "line" + return B(C, {}, [ + B('p', {}, [`#{name}`]), + B('pre', {}, [B('code', { + class: "language-mint" }, [ B('span', { - className: "string" - }, [`"First line" \\`]), - ` + className: "line" + }, [`\`Something\` +`]), + B('span', { + className: "line" + }, [ + ``, + B('span', { + className: "string" + }, [`"#{`]), + B('span', { + className: "variable" + }, [`name`]), + B('span', { + className: "string" + }, [`}"`]), + ` ` - ]), - B('span', { - className: "line" - }, [ - ``, + ]), B('span', { - className: "string" - }, [`"Second line" \\`]), - ` + className: "line" + }, [ + ``, + B('span', { + className: "string" + }, [`"First line" \`]), + ` ` - ]), - B('span', { - className: "line" - }, [ - ``, + ]), + B('span', { + className: "line" + }, [ + ``, + B('span', { + className: "string" + }, [`"Second line" \`]), + ` +` + ]), B('span', { - className: "string" - }, [`"Third line"`]) - ]) - ])])]) + className: "line" + }, [ + ``, + B('span', { + className: "string" + }, [`"Third line"`]) + ]) + ])]) + ]) }; diff --git a/spec/compilers2/store_with_get b/spec/compilers2/store_with_get index a7502efad..ae643d762 100644 --- a/spec/compilers2/store_with_get +++ b/spec/compilers2/store_with_get @@ -11,6 +11,7 @@ component Main { fun render : String { xxx + Test.hello } } -------------------------------------------------------------------------------- @@ -22,6 +23,7 @@ export const return `hello` }, B = () => { - return a.value + a.value; + return b() }; diff --git a/spec/compilers2/string_literal_escaped b/spec/compilers2/string_literal_escaped index 9d563c800..5df330860 100644 --- a/spec/compilers2/string_literal_escaped +++ b/spec/compilers2/string_literal_escaped @@ -5,5 +5,5 @@ component Main { } -------------------------------------------------------------------------------- export const A = () => { - return `Hello There \"Joe\"` + return `Hello There "Joe"` }; diff --git a/spec/compilers2/string_literal_with_escaped_interpolation b/spec/compilers2/string_literal_with_escaped_interpolation new file mode 100644 index 000000000..f05bee66c --- /dev/null +++ b/spec/compilers2/string_literal_with_escaped_interpolation @@ -0,0 +1,9 @@ +component Main { + fun render : String { + "Hello There \#{name}" + } +} +-------------------------------------------------------------------------------- +export const A = () => { + return `Hello There #{name}` +}; diff --git a/src/compiler2/js.cr b/src/compiler2/js.cr index 051fc3428..7007127f8 100644 --- a/src/compiler2/js.cr +++ b/src/compiler2/js.cr @@ -26,7 +26,7 @@ module Mint end def string(value : String) : Compiled - ["`", Raw.new(value), "`"] of Item + ["`", Raw.new(value.gsub('`', "\\`").gsub("${", "\\${`")), "`"] of Item end # Renders an object destructuring. diff --git a/src/compiler2/program.cr b/src/compiler2/program.cr index 1d9617b05..9dfbb0201 100644 --- a/src/compiler2/program.cr +++ b/src/compiler2/program.cr @@ -13,6 +13,33 @@ module Mint end end + def self.dbg_name(node) + case x = node + when Ast::Component + "<#{x.name.value}>" + when Ast::Module, Ast::Store, Ast::Provider + x.name.value + when Ast::Function, Ast::Constant, Ast::Get, Ast::State + "#{dbg_name(x.parent)}.#{x.name.value}" + when Ast::Block + "{block}" + when Ast::Access + "{access .#{x.field.value}}" + when Ast::Statement + name = + case target = x.target + when Ast::Variable + " #{target.value}" + end + + "{statement#{name}}" + when Ast::Route + "{route #{x.url}}" + else + x.class.name + end + end + def self.program( artifacts : TypeChecker::Artifacts, config : Config @@ -105,6 +132,25 @@ module Mint end end + # bundles.each do |node, items| + # entities = + # items.compact_map do |item| + # if item[0] == node + # next if node.is_a?(Ast::Defer) + # else + # next if item[0].is_a?(Ast::Component) && + # item[0].as(Ast::Component).async? + # end + + # dbg_name(item[0]) + # end + + # puts bundle_name(node) + # entities.sort.each do |item| + # puts " > #{item}" + # end + # end + # Add not already added items to the main bundle. bundles[nil].concat(compiler.compiled) diff --git a/src/compiler2/utils.cr b/src/compiler2/utils.cr index ae9805f28..485234b64 100644 --- a/src/compiler2/utils.cr +++ b/src/compiler2/utils.cr @@ -59,14 +59,14 @@ module Mint parts.map do |item| case item in String - ["`", Raw.new(item.escape_for_javascript), "`"] of Item + js.string(item) in Tuple(SemanticTokenizer::TokenType, String) js.call(Builtin::CreateElement, [ [%("span")] of Item, js.object({"className".as(Item) => [ %("#{item[0].to_s.underscore}"), ] of Item}), - js.array([["`", Raw.new(item[1].escape_for_javascript), "`"] of Item]), + js.array([js.string(item[1])]), ]) end end diff --git a/src/compilers2/directives/format.cr b/src/compilers2/directives/format.cr index 66aa3430c..54349ce0f 100644 --- a/src/compilers2/directives/format.cr +++ b/src/compilers2/directives/format.cr @@ -9,8 +9,6 @@ module Mint Formatter.new .format(node.content, Formatter::BlockFormat::Naked) .gsub('\\', "\\\\") - .gsub('`', "\\`") - .gsub("${", "\\${") js.array([content, js.string(formatted)]) end diff --git a/src/compilers2/env.cr b/src/compilers2/env.cr index 7ec81c922..852b28023 100644 --- a/src/compilers2/env.cr +++ b/src/compilers2/env.cr @@ -3,7 +3,7 @@ module Mint def compile(node : Ast::Env) : Compiled compile node do value = - MINT_ENV[node.name].to_s.gsub('`', "\\`") + MINT_ENV[node.name].to_s js.string(value) end diff --git a/src/compilers2/here_doc.cr b/src/compilers2/here_doc.cr index 866256a9e..20c464819 100644 --- a/src/compilers2/here_doc.cr +++ b/src/compilers2/here_doc.cr @@ -27,6 +27,7 @@ module Mint .lchop('\r') .lchop("\n\r") .rstrip + .gsub("\\\#{", "\#{") if node.modifier == '#' document = diff --git a/src/compilers2/js.cr b/src/compilers2/js.cr index 9e20a4e7e..cabf68c58 100644 --- a/src/compilers2/js.cr +++ b/src/compilers2/js.cr @@ -15,10 +15,10 @@ module Mint value = node.value.flat_map do |entity| case entity - when Ast::Node + in Ast::Node compile entity - else - entity + in String + entity.gsub("\\`", '`') end end diff --git a/src/compilers2/string_literal.cr b/src/compilers2/string_literal.cr index 788488c92..5a82cae50 100644 --- a/src/compilers2/string_literal.cr +++ b/src/compilers2/string_literal.cr @@ -10,7 +10,9 @@ module Mint [ item .gsub('`', "\\`") - .gsub("${", "\\${"), + .gsub("${", "\\${") + .gsub("\\\"", "\"") + .gsub("\\\#{", "\#{"), ] end end diff --git a/src/formatters/js.cr b/src/formatters/js.cr index 321ef593e..2f234bdee 100644 --- a/src/formatters/js.cr +++ b/src/formatters/js.cr @@ -7,7 +7,7 @@ module Mint when Ast::Interpolation item.source else - format(item).gsub('`', "\\`") + format(item) end end diff --git a/src/parsers/js.cr b/src/parsers/js.cr index 61599c07d..fe63db6c9 100644 --- a/src/parsers/js.cr +++ b/src/parsers/js.cr @@ -6,7 +6,7 @@ module Mint value = many(parse_whitespace: false) do - raw('`').try(&.gsub("\\`", '`')) || interpolation + raw('`') || interpolation end next error :js_expected_closing_tick do diff --git a/src/parsers/operator.cr b/src/parsers/operator.cr index e71d44314..37b303a59 100644 --- a/src/parsers/operator.cr +++ b/src/parsers/operator.cr @@ -52,9 +52,14 @@ module Mint next unless operator next if operator != "|>" && !whitespace? - ast.operators << {saved_position, saved_position + operator.size} - whitespace + case operator + when "or" + ast.keywords << {saved_position, saved_position + operator.size} + else + ast.operators << {saved_position, saved_position + operator.size} + end + whitespace operator end end diff --git a/src/reactor.cr b/src/reactor.cr index 14f008d76..fdb8183f2 100644 --- a/src/reactor.cr +++ b/src/reactor.cr @@ -30,7 +30,7 @@ module Mint workspace = Workspace.current workspace.format = auto_format workspace.check_env = true - workspace.check_everything = true + workspace.check_everything = false workspace.on "change" do |result| case result diff --git a/src/semantic_tokenizer.cr b/src/semantic_tokenizer.cr index a70213581..042f760be 100644 --- a/src/semantic_tokenizer.cr +++ b/src/semantic_tokenizer.cr @@ -23,7 +23,6 @@ module Mint TOKEN_MAP = { Ast::TypeVariable => TokenType::TypeParameter, Ast::Comment => TokenType::Comment, - Ast::StringLiteral => TokenType::String, Ast::RegexpLiteral => TokenType::Regexp, Ast::NumberLiteral => TokenType::Number, Ast::Id => TokenType::Type, @@ -154,6 +153,46 @@ module Mint add(node.tag, TokenType::Namespace) end + def tokenize(node : Ast::StringLiteral) + if node.value.size == 0 + add(node, TokenType::String) + else + position = + node.from + + node.value.each_with_index do |item, index| + last = + index == (node.value.size - 1) + + case item + in Ast::Interpolation + # We skip interpolations because they will be process separately + # but we need to proceed the position to it's end, also we need + # to add `#{` as a string which is everything up to the boxed + # expressions start. + add(position, item.expression.from, TokenType::String) + position = item.expression.to + + if last + add(position, node.to, TokenType::String) + end + in String + from = + position + + position = + if last + node.to + else + position + item.size + end + + add(from, position, TokenType::String) + end + end + end + end + def tokenize(node : Ast::HtmlComponent) node.closing_tag_position.try do |position| add(position, position + node.component.value.size, :type) diff --git a/src/type_checker.cr b/src/type_checker.cr index 3980abc33..e43ae2aa9 100644 --- a/src/type_checker.cr +++ b/src/type_checker.cr @@ -89,14 +89,7 @@ module Mint def print_stack @stack.each_with_index do |i, index| - x = case i - when Ast::Component then "" - when Ast::Function then "" - when Ast::Block then "" - when Ast::Call then "" - else - i - end + x = Compiler2.dbg_name(i) if index == 0 puts x @@ -240,26 +233,44 @@ module Mint # Already tracked if cache[node]? - @ref_stack[node]?.try(&.each { |child| track_references(child) }) + @ref_stack[node]?.try(&.each do |child| + # If we hit a defer we break out of the loop + # since it will always be in a different file. + case child + when Ast::Defer + break + else + track_references(child) + end + end) end references[node] ||= Set(Ast::Node | Nil).new # case node - # when Ast::Function - # if node.name.value == "test" - # puts node - # puts "-----" + # when Ast::Constant + # if node.name.value == "INDEX" + # puts "TRACK STACK #{Compiler2.dbg_name(node)}" # print_stack + # @ref_stack[node]?.try(&.each { |child| Compiler2.dbg_name(child) }) # puts component_stack.size + # pp caller.reverse # end # end if component_stack.empty? references[node].add(nil) else - component_stack.each do |component| + component_stack.reverse_each do |component| references[node].add(component) + + # If we hit a defer we break out of the loop + # since it will always be that defers file ( + # which we added above) + case component + when Ast::Defer + break + end end end end diff --git a/src/type_checkers/if.cr b/src/type_checkers/if.cr index 90c9b4e3a..bb19f11b8 100644 --- a/src/type_checkers/if.cr +++ b/src/type_checkers/if.cr @@ -7,8 +7,11 @@ module Mint variables, await = case item = node.condition when Ast::Statement - if item.target.is_a?(Ast::TypeDestructuring) - {destructure(item.target.as(Ast::TypeDestructuring), condition), item.await} + case item.target + when Ast::TupleDestructuring, + Ast::ArrayDestructuring, + Ast::TypeDestructuring + {destructure(item.target, condition), item.await} end end || {[] of VariableScope, false} diff --git a/src/utils/markd_vdom_renderer2.cr b/src/utils/markd_vdom_renderer2.cr index effc4dc40..8e2864d8a 100644 --- a/src/utils/markd_vdom_renderer2.cr +++ b/src/utils/markd_vdom_renderer2.cr @@ -36,11 +36,12 @@ module Mint if node == separator replacements.shift else - ["`", Raw.new(node.escape_for_javascript), "`"] of Item + js.string(node) end in Node attributes = - node.attributes + node + .attributes .transform_values { |value| [%("#{value}")] of Item } children =