@@ -159,32 +159,75 @@ def check_location_yaml(repo_root: Path) -> list[dict]:
159159 return findings
160160
161161
162- def run_djlint (skeleton_dir : Path ) -> list [dict ]:
163- import shutil
162+ # djlint defaults to --extension=html; scaffolder skeletons are mostly non-HTML.
163+ SKELETON_LINT_SKIP_SUFFIXES = frozenset (
164+ {
165+ ".png" ,
166+ ".jpg" ,
167+ ".jpeg" ,
168+ ".gif" ,
169+ ".webp" ,
170+ ".ico" ,
171+ ".svg" ,
172+ ".pdf" ,
173+ ".zip" ,
174+ ".tar" ,
175+ ".gz" ,
176+ ".bz2" ,
177+ ".xz" ,
178+ ".7z" ,
179+ ".jar" ,
180+ ".war" ,
181+ ".ear" ,
182+ ".class" ,
183+ ".so" ,
184+ ".dll" ,
185+ ".dylib" ,
186+ ".exe" ,
187+ ".bin" ,
188+ ".dat" ,
189+ ".woff" ,
190+ ".woff2" ,
191+ ".ttf" ,
192+ ".eot" ,
193+ ".mp3" ,
194+ ".mp4" ,
195+ ".avi" ,
196+ ".mov" ,
197+ ".wav" ,
198+ }
199+ )
164200
165- if not shutil .which ("djlint" ):
166- return [
167- {
168- "check" : "nunjucks_lint" ,
169- "severity" : "info" ,
170- "message" : "djlint not installed — skipping Nunjucks lint" ,
171- }
172- ]
173- cmd = [
174- "djlint" ,
175- str (skeleton_dir ),
176- "--profile=jinja" ,
177- "--lint" ,
178- "--quiet" ,
179- ]
180- try :
181- proc = subprocess .run (cmd , capture_output = True , text = True , check = False )
182- except OSError as exc :
201+
202+ def collect_skeleton_lint_targets (skeleton_dir : Path ) -> tuple [set [str ], list [Path ]]:
203+ """Return (extensions, extensionless files) djlint should lint under skeleton/."""
204+ extensions : set [str ] = set ()
205+ extensionless : list [Path ] = []
206+ for path in skeleton_dir .rglob ("*" ):
207+ if not path .is_file ():
208+ continue
209+ suffix = path .suffix .lower ()
210+ if suffix in SKELETON_LINT_SKIP_SUFFIXES :
211+ continue
212+ try :
213+ path .read_text (encoding = "utf-8" )
214+ except (UnicodeDecodeError , OSError ):
215+ continue
216+ if suffix :
217+ extensions .add (suffix .lstrip ("." ))
218+ else :
219+ extensionless .append (path )
220+ return extensions , sorted (extensionless )
221+
222+
223+ def _parse_djlint_output (proc : subprocess .CompletedProcess ) -> list [dict ]:
224+ combined = f"{ proc .stdout } \n { proc .stderr } "
225+ if "No files to check" in combined :
183226 return [
184227 {
185228 "check" : "nunjucks_lint" ,
186- "severity" : "info " ,
187- "message" : f "djlint skipped: { exc } " ,
229+ "severity" : "warning " ,
230+ "message" : "djlint found no files to check for this target " ,
188231 }
189232 ]
190233 if proc .returncode == 0 :
@@ -212,6 +255,75 @@ def run_djlint(skeleton_dir: Path) -> list[dict]:
212255 return findings
213256
214257
258+ def _run_djlint_cmd (cmd : list [str ]) -> list [dict ]:
259+ try :
260+ proc = subprocess .run (cmd , capture_output = True , text = True , check = False )
261+ except OSError as exc :
262+ return [
263+ {
264+ "check" : "nunjucks_lint" ,
265+ "severity" : "info" ,
266+ "message" : f"djlint skipped: { exc } " ,
267+ }
268+ ]
269+ return _parse_djlint_output (proc )
270+
271+
272+ def run_djlint (skeleton_dir : Path ) -> list [dict ]:
273+ import shutil
274+
275+ if not shutil .which ("djlint" ):
276+ return [
277+ {
278+ "check" : "nunjucks_lint" ,
279+ "severity" : "info" ,
280+ "message" : "djlint not installed — skipping Nunjucks lint" ,
281+ }
282+ ]
283+
284+ extensions , extensionless = collect_skeleton_lint_targets (skeleton_dir )
285+ if not extensions and not extensionless :
286+ file_count = sum (1 for path in skeleton_dir .rglob ("*" ) if path .is_file ())
287+ if file_count :
288+ return [
289+ {
290+ "check" : "nunjucks_lint" ,
291+ "severity" : "info" ,
292+ "message" : "No readable text skeleton files to lint" ,
293+ }
294+ ]
295+ return [
296+ {
297+ "check" : "nunjucks_lint" ,
298+ "severity" : "info" ,
299+ "message" : "Skeleton directory is empty — nothing to lint" ,
300+ }
301+ ]
302+
303+ findings : list [dict ] = []
304+ for ext in sorted (extensions ):
305+ cmd = [
306+ "djlint" ,
307+ str (skeleton_dir ),
308+ "-e" ,
309+ ext ,
310+ "--profile=jinja" ,
311+ "--lint" ,
312+ "--quiet" ,
313+ ]
314+ findings .extend (_run_djlint_cmd (cmd ))
315+ for path in extensionless :
316+ cmd = [
317+ "djlint" ,
318+ str (path ),
319+ "--profile=jinja" ,
320+ "--lint" ,
321+ "--quiet" ,
322+ ]
323+ findings .extend (_run_djlint_cmd (cmd ))
324+ return findings
325+
326+
215327def validate_template (
216328 path : Path , * , check_repo : bool , lint_skeleton : bool , use_jsonschema : bool = True
217329) -> dict :
0 commit comments