Skip to main content

Add a function for writing to a JavaScript file

ID
047f653
date
2024-08-16 21:23:43+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
8be8cda
message
Add a function for writing to a JavaScript file
changed files
3 files, 55 additions, 2 deletions

Changed files

README.md (486) → README.md (554)

diff --git a/README.md b/README.md
index 1b3e83b..8c55b91 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # python-js-files
 
-This is a collection of Python functions for manipulating JavaScript "data files" -- that is, JavaScript files that have a single variable that defines a JSON value.
+This is a collection of Python functions for manipulating JavaScript "data files" -- that is, JavaScript files that define a single variable with a JSON value.
 
 This is an example of a JavaScript data file:
 
@@ -13,6 +13,7 @@ Think of this module as the JSON module, but for JavaScript files.
 ## API
 
 *   You can read a JavaScript file with `read_js(path, varname)`
+*   You can write a JavaScript file with `write_js(path, value, varname)`
 
 ## Why not use JSON files?
 

src/javascript/__init__.py (940) → src/javascript/__init__.py (1565)

diff --git a/src/javascript/__init__.py b/src/javascript/__init__.py
index ad71cd6..1d8a43d 100644
--- a/src/javascript/__init__.py
+++ b/src/javascript/__init__.py
@@ -34,3 +34,24 @@ def read_js(p: pathlib.Path | str, *, varname: str) -> typing.Any:
     json_string = contents.replace(f"const {varname} = ", "").rstrip().rstrip(";")
 
     return json.loads(json_string)
+
+
+def write_js(p: pathlib.Path | str, *, value: typing.Any, varname: str) -> None:
+    """
+    Write a JavaScript "data file".
+
+    Example:
+
+        >>> red_pentagon = {'sides': 5, 'colour': 'red'}
+        >>> write_js('shape.js', value=red_pentagon, varname='redPentagon')
+        >>> open('shape.js').read()
+        'const redPentagon = {\n  "sides": 5,\n  "colour": "red"\n};\n'
+
+    """
+    json_string = json.dumps(value, indent=2)
+    js_string = f"const {varname} = {json_string};\n"
+
+    pathlib.Path(p).parent.mkdir(exist_ok=True, parents=True)
+
+    with open(p, "w") as out_file:
+        out_file.write(js_string)

tests/test_javascript.py (1569) → tests/test_javascript.py (2611)

diff --git a/tests/test_javascript.py b/tests/test_javascript.py
index afb03a8..cc2e94d 100644
--- a/tests/test_javascript.py
+++ b/tests/test_javascript.py
@@ -2,7 +2,7 @@ import pathlib
 
 import pytest
 
-from javascript import read_js
+from javascript import read_js, write_js
 
 
 class TestReadJs:
@@ -42,3 +42,34 @@ class TestReadJs:
 
         with pytest.raises(ValueError, match="does not end with a trailing semicolon"):
             read_js(js_path, varname="redPentagon")
+
+
+class TestWriteJs:
+    def test_can_write_file(self, tmp_path: pathlib.Path) -> None:
+        js_path = tmp_path / "shape.js"
+        red_pentagon = {"sides": 5, "colour": "red"}
+
+        write_js(js_path, value=red_pentagon, varname="redPentagon")
+
+        assert (
+            js_path.read_text()
+            == 'const redPentagon = {\n  "sides": 5,\n  "colour": "red"\n};\n'
+        )
+
+    def test_fails_if_cannot_write_file(self) -> None:
+        red_pentagon = {"sides": 5, "colour": "red"}
+
+        with pytest.raises(FileExistsError):
+            write_js("/", value=red_pentagon, varname="redPentagon")
+
+    def test_creates_parent_directory(self, tmp_path: pathlib.Path) -> None:
+        js_path = tmp_path / "1/2/3/shape.js"
+        red_pentagon = {"sides": 5, "colour": "red"}
+
+        write_js(js_path, value=red_pentagon, varname="redPentagon")
+
+        assert js_path.exists()
+        assert (
+            js_path.read_text()
+            == 'const redPentagon = {\n  "sides": 5,\n  "colour": "red"\n};\n'
+        )