k0kubun / hamlit

High Performance Haml Implementation
https://rubygems.org/gems/hamlit
Other
980 stars 60 forks source link

Haml checker CLI #166

Closed knightq closed 3 years ago

knightq commented 3 years ago

Hi,

is there any chance to have a bare syntax checker for HAML similar to:

haml -c <haml_path>

to be used on a deployment pipeline to just check syntax of HAML?

It would be useful if that command will exit with a status other than zero.

haml -c works, but hamlit would be way faster than it (I suspect)...

I.e

# This works, but use haml and it is quite slow...
for FILE_HAML in $HAML_FILES; do
  haml -c $FILE_HAML || echo "Syntax error on file $FILE_HAML"
done

# Something like this would be better, if it's faster than the previous one...
for FILE_HAML in $HAML_FILES; do
  hamlit -c $FILE_HAML || echo "Syntax error on file $FILE_HAML"
done
WaKeMaTTa commented 3 years ago

You can use one of this 3 commands:

$ hamlit compile file.haml
$ hamlit parse file.haml
$ hamlit render file.haml

(I think parse is the fastest)

knightq commented 3 years ago

Well, unfortunately those commands returns 0 both if succeed or not (and that's useless on a pipeline syntax check)

$> hamlit -c right_syntax_file.haml
$> echo $?
0

$> haml -c wrong_syntax_file.haml
$> echo $?
1

haml does return other than 0 on syntax errors, hamlit does not:

hamlit parse right_syntax_file.haml
$> echo $?
0

hamlit parse right_syntax_file.haml
$> echo $?
0
WaKeMaTTa commented 3 years ago

You are right. Use complie or render, this 2 they raise an error and the command returns 1.

$ hamlit c wrong.haml
...
hamlit-2.13.0/lib/hamlit/compiler/script_compiler.rb:57:in `eval': (eval):1: unterminated string meets end of file (SyntaxError)

$ echo $?
1
knightq commented 3 years ago

... not exactly.

hamlit compile does not return 1 on errors.

Compile (exit 0)

hamlit c app/views/activities/_form_planning.html.haml
_buf = [];
;
;
;
;
; raise Hamlit::HamlSyntaxError.new(%q[Inconsistent indentation: 6 spaces used for indentation, but the rest of the document was indented using 4 spaces.], 5); _buf = _buf.join("".freeze)
$> echo $?
0

Parse (exit 0)

$> hamlit p wrong.html.haml
#<Hamlit::HamlSyntaxError: Inconsistent indentation: 6 spaces used for indentation, but the rest of the document was indented using 4 spaces.>
$> echo $?
0

Render (exit 1)

$> hamlit r wrong.html.haml
Traceback (most recent call last):
        9: from /Users/andreasalicetti/.rbenv/versions/2.6.6/bin/hamlit:23:in `<main>'
        8: from /Users/andreasalicetti/.rbenv/versions/2.6.6/bin/hamlit:23:in `load'
        7: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/hamlit-2.13.0/exe/hamlit:6:in `<top (required)>'
        6: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/thor-1.0.1/lib/thor/base.rb:485:in `start'
        5: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch'
        4: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
        3: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
        2: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/hamlit-2.13.0/lib/hamlit/cli.rb:16:in `render'
        1: from /Users/andreasalicetti/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/hamlit-2.13.0/lib/hamlit/cli.rb:16:in `eval'
(eval):6:in `render': Inconsistent indentation: 6 spaces used for indentation, but the rest of the document was indented using 4 spaces. (Hamlit::HamlSyntaxError)
$> echo $?
1

(BTW, my hamlit version is 2.13.0)

The only one that returns an error code consistent with the fact that the given input is invalid is hamlit render. Unfortunately, that one is the slowest one (maybe also slower than haml c) and that may cause false negative due to the absence of resource for rendering (infact it does not perform just a simple syntax check: it tries to actually produce the HTML by resolving all the statements).

It would be great if even the simpler (and faster) hamlit c would return 1 on compilation error.

WaKeMaTTa commented 3 years ago

Maybe what you want is this other gem haml_lint

knightq commented 3 years ago

I'll give it a try!

My goal is just to find a syntax checker (that will detect only syntax errors on HAML files) faster than haml c (if any).

That's why I was looking at hamlit.

Thank you anyway, have a good day!

knightq commented 3 years ago

Just tried: haml-lint is way slower than haml (make sense, since it does more things other than syntax checking and uses haml as gem dependency).

$> time haml -c valid.html.haml
Syntax OK
real    0m0.182s
user    0m0.160s
sys     0m0.013s
$> time haml-lint -i FinalNewline --fail-fast --fail-level error valid.html.haml
1 file inspected, 0 lints detected
real    0m0.724s
user    0m0.650s
sys     0m0.060s
WaKeMaTTa commented 3 years ago

No more ideas. Good luck!

k0kubun commented 3 years ago

A quick alternative is hamlit compile valid.html.haml | ruby -c, which I believe is almost the same. However, as it is Haml's built-in feature, I'm okay for supporting -c too.

oldgiova commented 3 years ago

Hi everyone, I have the very same need and tried @k0kubun solution: it works ok but still slower than haml -c:

# hamlit way
▶ time (hamlit compile _form.html.haml | ruby -c)
-:181: syntax error, ...
 0.59s user 0.28s system 114% cpu 0.758 total

# haml -c way, much faster
▶ time haml -c _form.html.haml
_form.html.haml:181: syntax error, ...
0.19s user 0.08s system 96% cpu 0.278 total

thank you Roberto

k0kubun commented 3 years ago

Hmm, it seems compilation itself is not that slow, but loading Hamlit and the CLI seems slow:

$ cat /tmp/a.haml
%a{ hello: world }

$ time hamlit version
2.13.0
hamlit version  0.80s user 0.64s system 98% cpu 1.459 total

$ time hamlit compile /tmp/a.haml
_buf = []; _buf << (%Q<a hello='#{::Hamlit::Utils.escape_html((world))}); _buf << ("'></a>\n".freeze); _buf = _buf.join("".freeze)
hamlit compile /tmp/a.haml  0.80s user 0.64s system 98% cpu 1.460 total

If your current problem is the performance, I think we need to optimize the general CLI performance first before considering -c, because it's gonna be slow anyway without it.

By the way, let me take a look at this after Dec 25th, because right now I'm focusing on Ruby 3 release.

knightq commented 3 years ago

Thank you @k0kubun 🙏🏻

k0kubun commented 3 years ago

OK, I optimized hamlit commands at v2.13.2.

$ cat /tmp/a.haml
%a{ hello: world }

$ hamlit version
2.13.1
$ time (hamlit compile /tmp/a.haml | ruby -c)
Syntax OK
( hamlit compile /tmp/a.haml | ruby -c; )  0.61s user 0.14s system 107% cpu 0.695 total

$ hamlit version
2.13.2
$ time (hamlit compile /tmp/a.haml | ruby -c)
Syntax OK
( hamlit compile /tmp/a.haml | ruby -c; )  0.20s user 0.06s system 123% cpu 0.207 total

$ time haml -c /tmp/a.haml
Syntax OK
haml -c /tmp/a.haml  0.13s user 0.04s system 100% cpu 0.166 total

Could you check if hamlit compile x.haml | ruby -c is fast enough on your environment with Hamlit 2.13.2? I can support the -c option directly, but because ruby -c is fast, supporting it will probably not contribute to the check speed. If you still need it for simplicity or compatibility reasons, please let me know.

oldgiova commented 3 years ago

Hi K0kobun, thanks for your effort, now hamlit is much faster. Unfortunately it's still slower than haml -c so we can't still use it in our pipelines:

In a check of about 1500 files:

#haml -c
time (find . -type f -iname '*.haml' -exec haml -c {} \;)
...
289.95s user 135.90s system 95% cpu 7:27.78 total

#hamlit compile <file> | ruby -c
time ~/hamlit-script.sh
...
~/hamlit-script.sh  487.91s user 231.93s system 130% cpu 9:10.04 total

hamlit-script.sh is a simple bash for:

FILES=$(find . -type f -iname '*.haml')
for FILE in ${FILES}; do
  hamlit compile ${FILE} | ruby -c
done

Thanks again, Roberto

k0kubun commented 3 years ago

Could you check the performance of hamlit compile alone? Would you be satisfied with it or not? The answer would change what should be fixed.

oldgiova commented 3 years ago

Sorry for late, performance of hamlit compile alone is satisfactory. Thanks for your help

k0kubun commented 3 years ago

Released hamlit compile -c as v2.14.1. Please try it.

oldgiova commented 3 years ago

Thanks, I just tried with 1500 files:

# haml -c
( find . -type f -iname '*.haml' -exec haml -c {} \;; )  
...
316.03s user 156.37s system 96% cpu 8:07.57 total

# hamlit compile <file> | ruby -c
...
~/hamlit-script.sh  535.82s user 269.47s system 126% cpu 10:37.17 total

and also tried with a single file:

time (hamlit compile summary.html.haml| ruby -c)
Syntax OK
( hamlit compile summary.html.haml | ruby -c; )  0.31s user 0.14s system 130% cpu 0.348 total

time haml -c summary.html.haml
Syntax OK
haml -c summary.html.haml  0.17s user 0.08s system 96% cpu 0.265 total

Actually we can use both solution in our pipeline because we're checking not all 1500 files but just the modified one after the last merge.

Thanks for your help Roberto