diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 3ed5173..bfc2085 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -49,7 +49,7 @@ STRINGS_CID="$(docker create strings:latest)" \ && docker rm "$STRINGS_CID" \ && tar czvf strings.linux.tar.gz strings.linux lib -# Trying it on Ubuntu 18.04 +# Trying it on Ubuntu 20.04 docker run -it --rm \ -v "$(pwd):/app" \ -v "$(realpath "$(pwd)/../group-income-simple"):/repo" \ diff --git a/Dockerfile b/Dockerfile index c51babe..73c32dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,11 +20,8 @@ RUN echo '=== Installing QuickJS ===' \ RUN echo '=== Installing Flow ===' \ && git clone --branch v0.183.1 --depth 1 https://github.com/facebook/flow.git flow -RUN echo '=== Installing TypeScript ===' \ - && npm install --no-save typescript browserify - -RUN echo '=== Installing Pug ===' \ - && npm install --no-save pug-lexer pug-parser pug-walk +RUN echo '=== Installing JS dependencies ===' \ + && npm install --no-save typescript browserify pug-lexer pug-parser pug-walk COPY src src COPY dune dune diff --git a/src/cli/strings.ml b/src/cli/strings.ml index 4689f9c..3eb18e3 100644 --- a/src/cli/strings.ml +++ b/src/cli/strings.ml @@ -143,7 +143,12 @@ let write_english english = Lwt_io.with_file ~flags:write_flags ~mode:Output path_json (fun oc_json -> let* () = Lwt.join [ Lwt_io.write oc_strings header; Lwt_io.write_char oc_json '{' ] in let* () = - String.Table.fold english ~init:Lwt.return_unit ~f:(fun ~key ~data acc -> + (* Switch to a map to preserve order as much as possible and therefore reduce merge conflicts *) + let map = + String.Table.fold english ~init:String.Map.empty ~f:(fun ~key ~data acc -> + String.Map.set acc ~key ~data) + in + String.Map.fold map ~init:Lwt.return_unit ~f:(fun ~key ~data acc -> let fmt_key = fmt key in let output_strings = sprintf "/* %s */\n%s = %s;\n\n" data fmt_key fmt_key in let output_json = json_pair fmt_key fmt_key first in diff --git a/src/cli/vue.ml b/src/cli/vue.ml index e4ecc56..a067d5f 100644 --- a/src/cli/vue.ml +++ b/src/cli/vue.ml @@ -102,7 +102,9 @@ let debug_template ~path languages template_script target = collect_from_possible_scripts collector template_script ~on_string:(Queue.enqueue strings) in let buf = Buffer.create 256 in - Queue.iter strings ~f:(fun s -> bprintf buf "%s\n" s); + let deduped = Queue.fold strings ~init:String.Set.empty ~f:String.Set.add in + let* () = Lwt_io.printlf "Found %d strings:" (String.Set.length deduped) in + String.Set.iter deduped ~f:(fun s -> bprintf buf !"%{Yojson.Basic}\n" (`String s)); if not (Queue.is_empty file_errors) then ( bprintf buf "\n❌ %s errors in %s:\n" error_kind path; diff --git a/src/parsing/pug.ml b/src/parsing/pug.ml index 0a9e0d3..2371731 100644 --- a/src/parsing/pug.ml +++ b/src/parsing/pug.ml @@ -118,8 +118,8 @@ let parser = lift3 (fun prefix identifier contents -> { prefix; identifier; contents }) (maybe (symbols [ ":$"; ":"; "@"; "#" ])) - (identifier <* blank) - (maybe (char '=' *> blank *> pug_string)) + identifier + (maybe (mlblank *> char '=' *> mlblank *> pug_string)) in let at_least_indent indent = string (String.make indent ' ') in diff --git a/src/quickjs/parsers.js b/src/quickjs/parsers.js index ba39dde..d9c50ac 100644 --- a/src/quickjs/parsers.js +++ b/src/quickjs/parsers.js @@ -41,13 +41,13 @@ const walk = require('pug-walk') globalThis.extractFromPug = function (code) { const ast = parse(lex(code)) - const strings = [] - const possibleScripts = [] + const accStrings = {} + const accPossibleScripts = [] const addAttr = function (s) { if (typeof s !== 'string' || s === '') { return } const parsed = extractFromAttr(s) - if (parsed !== '') { - possibleScripts.push(parsed) + if (parsed != null && parsed !== '') { + accPossibleScripts.push(parsed) } } walk(ast, function before (node, replace) { @@ -58,22 +58,32 @@ globalThis.extractFromPug = function (code) { if (node.name === 'i18n') { node.block.nodes.forEach(({ type, val }) => { if (type === 'Text' && val !== '') { - strings.push(val) + accStrings[val] = true } }) } break + case 'Block': + node.nodes.forEach(({ type, val }) => { + if (type === 'Text' && val !== '') { + accPossibleScripts.push(val) + } + }) + break case 'Code': - possibleScripts.push(node.val) + accPossibleScripts.push(node.val) break case 'InterpolatedTag': - possibleScripts.push(node.expr) + accPossibleScripts.push(node.expr) break case 'Conditional': case 'While': - possibleScripts.push(node.test) + accPossibleScripts.push(node.test) break } }) - return { strings, possibleScripts } + return { + strings: Object.keys(accStrings), + possibleScripts: accPossibleScripts.filter((s) => accStrings[s] == null) + } }