Skip to content

🏠 Home | ⚙️ API Reference | 🧱 Static Compiler

Static Compiler API

Reference for the pure-Python asset compilation helpers in codex_core.dev.static_compiler.

StaticCompiler

StaticCompiler

Orchestrates CSS and JS compilation.

Parameters:

Name Type Description Default
css bool

Enable CSS compilation (default True).

True
js bool

Enable JS compilation (default True).

True
minify bool

Full minification — removes comments + collapses whitespace.

False
remove_comments bool

Remove comments only, keep formatting.

True
Source code in src/codex_core/dev/static_compiler/compiler.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
class StaticCompiler:
    """Orchestrates CSS and JS compilation.

    Args:
        css: Enable CSS compilation (default True).
        js:  Enable JS compilation (default True).
        minify: Full minification — removes comments + collapses whitespace.
        remove_comments: Remove comments only, keep formatting.
    """

    def __init__(
        self,
        css: bool = True,
        js: bool = True,
        minify: bool = False,
        remove_comments: bool = True,
    ) -> None:
        self._css = CSSCompiler() if css else None
        self._js = JSCompiler() if js else None
        self._minify = minify
        self._remove_comments = remove_comments

    # ── Single project ────────────────────────────────────────────────────────

    def compile_from_config(
        self,
        config: Path,
        css_dir: Path | None = None,
        js_dir: Path | None = None,
    ) -> bool:
        """Compile one project from its compiler_config.json.

        css_dir and js_dir default to the directory containing the config file.
        """
        if not config.exists():
            print(f"ERROR: config not found: {config}")
            return False

        try:
            data = json.loads(config.read_text(encoding="utf-8"))
        except json.JSONDecodeError as e:
            print(f"ERROR: invalid JSON in {config}: {e}")
            return False

        base_dir = config.parent
        css_dir = css_dir or base_dir
        js_dir = js_dir or base_dir

        # Detect old {"base.css": "app.css"} format — treat as css-only
        css_config: dict[str, str] = data.get("css", {})
        js_config: dict[str, list[str] | dict[str, Any]] = data.get("js", {})
        if not css_config and not js_config:
            css_config = data

        ok = True

        if css_config and self._css:
            print("--- CSS ---")
            for src, dst in css_config.items():
                src_path = css_dir / src
                dst_path = css_dir / dst
                if not src_path.exists():
                    print(f"  WARNING: skipping {src} — file not found")
                    continue
                if not self._css.compile(
                    src_path,
                    dst_path,
                    minify=self._minify,
                    remove_comments=self._remove_comments,
                ):
                    ok = False

        if js_config and self._js:
            print("--- JS ---")
            for dst, bundle in js_config.items():
                dst_path = js_dir / dst
                if not self._js.compile_bundle(
                    bundle,
                    dst_path,
                    js_dir,
                    minify=self._minify,
                    remove_comments=self._remove_comments,
                ):
                    ok = False

        return ok

    # ── Multi-project (landings) ───────────────────────────────────────────────

    def compile_from_settings(self, settings: Path) -> bool:
        """Compile multiple projects listed in a master settings JSON file."""
        if not settings.exists():
            print(f"ERROR: settings not found: {settings}")
            return False

        try:
            data = json.loads(settings.read_text(encoding="utf-8"))
        except json.JSONDecodeError as e:
            print(f"ERROR: invalid JSON in {settings}: {e}")
            return False

        projects: list[dict[str, Any]] = data.get("projects", [])
        if not projects:
            print("WARNING: no projects defined in settings")
            return True

        root = settings.parent
        ok = True

        for project in projects:
            name = project.get("name", "unnamed")
            config_rel = project.get("config")
            if not config_rel:
                print(f"  WARNING: project '{name}' has no 'config' key, skipping")
                continue

            config_path = (root / config_rel).resolve()
            css_dir = (root / project["css_dir"]).resolve() if "css_dir" in project else None
            js_dir = (root / project["js_dir"]).resolve() if "js_dir" in project else None

            print(f"\n=== Project: {name} ===")
            if not self.compile_from_config(config_path, css_dir, js_dir):
                ok = False

        return ok

Functions

compile_from_config(config, css_dir=None, js_dir=None)

Compile one project from its compiler_config.json.

css_dir and js_dir default to the directory containing the config file.

Source code in src/codex_core/dev/static_compiler/compiler.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def compile_from_config(
    self,
    config: Path,
    css_dir: Path | None = None,
    js_dir: Path | None = None,
) -> bool:
    """Compile one project from its compiler_config.json.

    css_dir and js_dir default to the directory containing the config file.
    """
    if not config.exists():
        print(f"ERROR: config not found: {config}")
        return False

    try:
        data = json.loads(config.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        print(f"ERROR: invalid JSON in {config}: {e}")
        return False

    base_dir = config.parent
    css_dir = css_dir or base_dir
    js_dir = js_dir or base_dir

    # Detect old {"base.css": "app.css"} format — treat as css-only
    css_config: dict[str, str] = data.get("css", {})
    js_config: dict[str, list[str] | dict[str, Any]] = data.get("js", {})
    if not css_config and not js_config:
        css_config = data

    ok = True

    if css_config and self._css:
        print("--- CSS ---")
        for src, dst in css_config.items():
            src_path = css_dir / src
            dst_path = css_dir / dst
            if not src_path.exists():
                print(f"  WARNING: skipping {src} — file not found")
                continue
            if not self._css.compile(
                src_path,
                dst_path,
                minify=self._minify,
                remove_comments=self._remove_comments,
            ):
                ok = False

    if js_config and self._js:
        print("--- JS ---")
        for dst, bundle in js_config.items():
            dst_path = js_dir / dst
            if not self._js.compile_bundle(
                bundle,
                dst_path,
                js_dir,
                minify=self._minify,
                remove_comments=self._remove_comments,
            ):
                ok = False

    return ok

compile_from_settings(settings)

Compile multiple projects listed in a master settings JSON file.

Source code in src/codex_core/dev/static_compiler/compiler.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def compile_from_settings(self, settings: Path) -> bool:
    """Compile multiple projects listed in a master settings JSON file."""
    if not settings.exists():
        print(f"ERROR: settings not found: {settings}")
        return False

    try:
        data = json.loads(settings.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        print(f"ERROR: invalid JSON in {settings}: {e}")
        return False

    projects: list[dict[str, Any]] = data.get("projects", [])
    if not projects:
        print("WARNING: no projects defined in settings")
        return True

    root = settings.parent
    ok = True

    for project in projects:
        name = project.get("name", "unnamed")
        config_rel = project.get("config")
        if not config_rel:
            print(f"  WARNING: project '{name}' has no 'config' key, skipping")
            continue

        config_path = (root / config_rel).resolve()
        css_dir = (root / project["css_dir"]).resolve() if "css_dir" in project else None
        js_dir = (root / project["js_dir"]).resolve() if "js_dir" in project else None

        print(f"\n=== Project: {name} ===")
        if not self.compile_from_config(config_path, css_dir, js_dir):
            ok = False

    return ok

CSSCompiler

CSSCompiler

Pure Python CSS compiler. No external dependencies.

Source code in src/codex_core/dev/static_compiler/css.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class CSSCompiler:
    """Pure Python CSS compiler. No external dependencies."""

    def read(self, file_path: Path) -> str:
        try:
            return file_path.read_text(encoding="utf-8")
        except Exception as e:
            print(f"  ERROR reading {file_path}: {e}")
            return ""

    def resolve_imports(self, css: str, base_path: Path) -> str:
        """Recursively resolves @import url('...') directives."""
        pattern = r"@import\s+url\(['\"](.+?)['\"]\)(?:\s+(.+?))?;"

        def replace(match: re.Match[str]) -> str:
            import_path = match.group(1)
            media_query = match.group(2)
            full_path = (base_path / import_path).resolve()
            if not full_path.exists():
                print(f"  WARNING: import not found: {full_path}")
                return f"/* Import not found: {import_path} */"
            content = self.read(full_path)
            content = self.resolve_imports(content, full_path.parent)
            if media_query:
                return f"/* From {import_path} */\n@media {media_query} {{\n{content}\n}}"
            return f"/* From {import_path} */\n{content}"

        return re.sub(pattern, replace, css)

    def remove_comments(self, css: str) -> str:
        return re.sub(r"/\*.*?\*/", "", css, flags=re.DOTALL)

    def minify(self, css: str) -> str:
        css = self.remove_comments(css)
        css = re.sub(r"\s+", " ", css)
        css = re.sub(r"\s*([{}:;,])\s*", r"\1", css)
        return css.strip()

    def compile(
        self,
        source: Path,
        output: Path,
        minify: bool = False,
        remove_comments: bool = False,
    ) -> bool:
        print(f"  CSS: {source.name} -> {output.name}")
        raw = self.read(source)
        if not raw:
            return False

        result = self.resolve_imports(raw, source.parent)
        if minify:
            result = self.minify(result)
        elif remove_comments:
            result = self.remove_comments(result)

        header = f"/*\n * Compiled CSS — DO NOT EDIT\n * Source: {source.name}\n * Minified: {minify}\n */\n\n"
        try:
            output.write_text(header + result, encoding="utf-8")
            print(f"     OK: {len(header + result):,} bytes")
            return True
        except Exception as e:
            print(f"  ERROR writing {output}: {e}")
            return False

Functions

resolve_imports(css, base_path)

Recursively resolves @import url('...') directives.

Source code in src/codex_core/dev/static_compiler/css.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def resolve_imports(self, css: str, base_path: Path) -> str:
    """Recursively resolves @import url('...') directives."""
    pattern = r"@import\s+url\(['\"](.+?)['\"]\)(?:\s+(.+?))?;"

    def replace(match: re.Match[str]) -> str:
        import_path = match.group(1)
        media_query = match.group(2)
        full_path = (base_path / import_path).resolve()
        if not full_path.exists():
            print(f"  WARNING: import not found: {full_path}")
            return f"/* Import not found: {import_path} */"
        content = self.read(full_path)
        content = self.resolve_imports(content, full_path.parent)
        if media_query:
            return f"/* From {import_path} */\n@media {media_query} {{\n{content}\n}}"
        return f"/* From {import_path} */\n{content}"

    return re.sub(pattern, replace, css)

JSCompiler

JSCompiler

Pure Python JS bundler (concatenation + optional comment removal).

Source code in src/codex_core/dev/static_compiler/js.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
class JSCompiler:
    """Pure Python JS bundler (concatenation + optional comment removal)."""

    _provides_re = re.compile(r"@provides\s+([A-Za-z0-9._-]+)")
    _depends_re = re.compile(r"@depends\s+([^\n\r*]+)")

    def remove_comments(self, js: str) -> str:
        js = re.sub(r"/\*.*?\*/", "", js, flags=re.DOTALL)
        js = re.sub(r"(?<!:)//[^\n]*", "", js)
        return js

    def minify(self, js: str) -> str:
        js = self.remove_comments(js)
        return re.sub(r"\s+", " ", js).strip()

    def compile(
        self,
        sources: list[str],
        output: Path,
        js_dir: Path,
        minify: bool = False,
        remove_comments: bool = False,
    ) -> bool:
        print(f"  JS:  [{', '.join(sources)}] -> {output.name}")
        parts: list[str] = []

        for rel_path in sources:
            full_path = (js_dir / rel_path).resolve()
            if not full_path.exists():
                print(f"  WARNING: source not found: {full_path}")
                parts.append(f"/* Source not found: {rel_path} */")
                continue
            try:
                content = full_path.read_text(encoding="utf-8")
                parts.append(f"/* === {rel_path} === */\n{content}")
            except Exception as e:
                print(f"  ERROR reading {full_path}: {e}")

        result = "\n\n".join(parts)
        if minify:
            result = self.minify(result)
        elif remove_comments:
            result = self.remove_comments(result)

        header = f"/*\n * Compiled JS — DO NOT EDIT\n * Sources: {', '.join(sources)}\n * Minified: {minify}\n */\n\n"
        try:
            output.parent.mkdir(parents=True, exist_ok=True)
            output.write_text(header + result, encoding="utf-8")
            print(f"     OK: {len(header + result):,} bytes")
            return True
        except Exception as e:
            print(f"  ERROR writing {output}: {e}")
            return False

    def compile_bundle(
        self,
        bundle: list[str] | dict[str, Any],
        output: Path,
        js_dir: Path,
        minify: bool = False,
        remove_comments: bool = False,
    ) -> bool:
        if isinstance(bundle, list):
            return self.compile(bundle, output, js_dir, minify=minify, remove_comments=remove_comments)

        strategy = bundle.get("strategy", "ordered")
        if strategy == "ordered":
            sources = bundle.get("sources") or bundle.get("entry") or []
            if not isinstance(sources, list):
                print("  ERROR: ordered JS bundle expects a list in 'sources' or 'entry'")
                return False
            return self.compile(sources, output, js_dir, minify=minify, remove_comments=remove_comments)

        if strategy == "dependency_graph":
            return self.compile_dependency_graph(bundle, output, js_dir, minify=minify, remove_comments=remove_comments)

        print(f"  ERROR: unknown JS bundle strategy: {strategy}")
        return False

    def compile_dependency_graph(
        self,
        bundle: dict[str, Any],
        output: Path,
        js_dir: Path,
        minify: bool = False,
        remove_comments: bool = False,
    ) -> bool:
        js_dir = js_dir.resolve()
        entry_paths = bundle.get("entry", [])
        roots = bundle.get("roots", [])

        if not isinstance(entry_paths, list) or not entry_paths:
            print("  ERROR: dependency_graph bundle requires a non-empty 'entry' list")
            return False
        if not isinstance(roots, list) or not roots:
            print("  ERROR: dependency_graph bundle requires a non-empty 'roots' list")
            return False

        modules: dict[str, JSModule] = {}
        modules_by_path: dict[str, JSModule] = {}
        errors = False

        def register_module(rel_path: str) -> None:
            nonlocal errors
            normalized = rel_path.replace("\\", "/")
            if normalized in modules_by_path:
                return
            full_path = (js_dir / normalized).resolve()
            if not full_path.exists():
                print(f"  ERROR: unknown entry/source: {normalized}")
                errors = True
                return

            try:
                content = full_path.read_text(encoding="utf-8")
            except Exception as exc:
                print(f"  ERROR reading {full_path}: {exc}")
                errors = True
                return

            provides = self._parse_provides(content)
            if not provides:
                print(f"  ERROR: missing dependency provider metadata in {normalized}")
                errors = True
                return
            if len(provides) > 1:
                print(f"  ERROR: multiple @provides declarations in {normalized}: {', '.join(provides)}")
                errors = True
                return

            provide_name = provides[0]
            if provide_name in modules:
                print(
                    f"  ERROR: duplicate provider {provide_name} in {normalized} and {modules[provide_name].rel_path}"
                )
                errors = True
                return

            module = JSModule(
                rel_path=normalized,
                full_path=full_path,
                provides=provide_name,
                depends=tuple(self._parse_depends(content)),
            )
            modules[provide_name] = module
            modules_by_path[normalized] = module

        for root in roots:
            root_path = (js_dir / root).resolve()
            if not root_path.exists():
                print(f"  ERROR: unknown root directory: {root}")
                errors = True
                continue
            for full_path in sorted(root_path.rglob("*.js")):
                rel_path = full_path.relative_to(js_dir).as_posix()
                register_module(rel_path)

        for entry_path in entry_paths:
            register_module(entry_path)

        if errors:
            return False

        ordered_modules: list[JSModule] = []
        visiting: set[str] = set()
        visited: set[str] = set()

        def visit(provider: str, stack: list[str]) -> bool:
            if provider in visited:
                return True
            if provider in visiting:
                cycle = " -> ".join(stack + [provider])
                print(f"  ERROR: dependency cycle detected: {cycle}")
                return False
            module = modules.get(provider)
            if module is None:
                print(f"  ERROR: missing dependency: {provider}")
                return False

            visiting.add(provider)
            for dependency in module.depends:
                if not visit(dependency, stack + [provider]):
                    return False
            visiting.remove(provider)
            visited.add(provider)
            ordered_modules.append(module)
            return True

        for entry_path in entry_paths:
            provider = modules_by_path.get(entry_path.replace("\\", "/"))
            if provider is None:
                print(f"  ERROR: unknown entry: {entry_path}")
                return False
            if not visit(provider.provides, []):
                return False

        ordered_sources = [module.rel_path for module in ordered_modules]
        return self.compile(ordered_sources, output, js_dir, minify=minify, remove_comments=remove_comments)

    def _parse_provides(self, content: str) -> list[str]:
        return self._provides_re.findall(content)

    def _parse_depends(self, content: str) -> list[str]:
        dependencies: list[str] = []
        for raw_line in self._depends_re.findall(content):
            for part in raw_line.split(","):
                dependency = part.strip()
                if dependency:
                    dependencies.append(dependency)
        return dependencies