# SPDX-License-Identifier: CC0-1.0 # # Process javascript files and resolve dependencies between them # # This file is part of Haketilo # # Copyright (C) 2021, Wojtek Kosior # # This program is free software: you can redistribute it and/or modify # it under the terms of the CC0 1.0 Universal License as published by # the Creative Commons Corporation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # CC0 1.0 Universal License for more details. function read_file(filename, imports_state, exports_state, line, record, result) { imports_state = "not_started" exports_state = "not_started" do { result = (getline line < filename) if (result < 0) { printf "error reading %s", filename exit 1 } if (imports_state == "started" && line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORT[[:space:]]+[_a-zA-Z][_a-zA-Z0-9]*[[:space:]]*$/) { record = line sub(/^([[:space:]]*\*[[:space:]]+)?IMPORT[[:space:]]+/, "", record) sub(/([[:space:]]+$)/, "", record) imports[filename,++import_counts[filename]] = record } if (imports_state == "started" && line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORTS_END[[:space:]]*$/) imports_state = "finished" if (imports_state == "not_started" && line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORTS_START[[:space:]]*$/) imports_state = "started" if (exports_state == "started" && line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORT[[:space:]]+[_a-zA-Z][_a-zA-Z0-9]*[[:space:]]*$/) { record = line sub(/^([[:space:]]*\*[[:space:]]+)?EXPORT[[:space:]]+/, "", record) sub(/([[:space:]]+$)/, "", record) if (record in exports) { printf "ERROR: '%s' exported by both %s and %s\n", exports[record], filename > "/dev/stderr" } provides[record] = filename exports[filename,++export_counts[filename]] = record } if (exports_state == "started" && line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORTS_END[[:space:]]*$/) exports_state = "finished" if (exports_state == "not_started" && line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORTS_START[[:space:]]*$/) exports_state = "started" } while (result > 0) if (imports_state == "started") { printf "ERROR: Unclosed IMPORTS list in '%s'\n", filename \ > "/dev/stderr" exit 1 } if (exports_state == "started") { printf "ERROR: Unclosed EXPORTS list in '%s'\n", filename \ > "/dev/stderr" exit 1 } close(filename) } function print_file(filename, line) { while ((getline line < filename) > 0) print(line) close(filename) } function print_imports_code(filename, i, count, import_name) { count = import_counts[filename] for (i = 1; i <= count; i++) { import_name = imports[filename,i] printf "const %s = window.killtheweb.%s;\n", import_name, import_name } } function print_exports_code(filename, i, count, export_name) { count = export_counts[filename] for (i = 1; i <= count; i++) { export_name = exports[filename,i] printf "window.killtheweb.%s = %s;\n", export_name, export_name } } function wrap_file(filename) { print "\"use strict\";\n\n({fun: (function() {\n" print_imports_code(filename) printf "\n\n" print_file(filename) printf "\n\n" print_exports_code(filename) print "\n})}).fun();" } function compute_dependencies(filename, i, count, import_name, next_file) { if (processed[filename] == "used") return 0 if (processed[filename] == "on_stack") { printf "import loop on %s\n", filename > "/dev/stderr" return 1 } processed[filename] = "on_stack" count = import_counts[filename] for (i = 1; i <= count; i++) { import_name = imports[filename,i] if (!(import_name in provides)) { printf "nothing exports %s, required by %s\n", import_name, filename > "/dev/stderr" return 1 } if (compute_dependencies(provides[import_name]) > 0) { printf "when satisfying %s for %s\n", import_name, filename > "/dev/stderr" return 1 } } processed[filename] = "used" print filename return 0 } function print_usage() { printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code FILENAME[...]\n", ARGV[0] > "/dev/stderr" exit 1 } function mock_exports_init() { provides["browser"] = "exports_init.js" provides["is_chrome"] = "exports_init.js" provides["is_mozilla"] = "exports_init.js" processed["exports_init.js"] = "used" } BEGIN { operation = ARGV[1] if (ARGC < 3) print_usage() root_filename = ARGV[2] for (i = 2; i < ARGC; i++) filenames[ARGV[i]] mock_exports_init() for (filename in filenames) { # A filename is allowed to appear multiple times in the list. # Let's only process it once. if (!(filename in processed)) read_file(filename) processed[filename] = "not_used" } if (operation == "script_dependencies") { print("exports_init.js") if (compute_dependencies(root_filename) > 0) exit 1 } else if (operation == "wrapped_code") { wrap_file(root_filename) } else { print_usage() } }