aboutsummaryrefslogtreecommitdiff
path: root/compute_scripts.awk
diff options
context:
space:
mode:
Diffstat (limited to 'compute_scripts.awk')
-rw-r--r--compute_scripts.awk196
1 files changed, 196 insertions, 0 deletions
diff --git a/compute_scripts.awk b/compute_scripts.awk
new file mode 100644
index 0000000..123106c
--- /dev/null
+++ b/compute_scripts.awk
@@ -0,0 +1,196 @@
+# 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()
+ }
+}