feat(tsl-codegen): streamline generation workflow

Add explicit project-root output routing and require named input options with structured examples.

Remove the unused HTML metadata enrichment command and its dedicated tests.
This commit is contained in:
csh
2026-08-13 15:35:05 +08:00
parent 8315f1060a
commit f90faa10c3
5 changed files with 140 additions and 1730 deletions
+104 -13
View File
@@ -164,19 +164,21 @@ class DocGenCliTest(unittest.TestCase):
self.assertEqual(0, result.returncode, result.stderr)
self.assertTrue(self.generated("base/my_functions").is_file())
def test_legacy_positional_input_remains_supported(self):
def test_positional_input_is_rejected(self):
result = self.run_raw_cli(self.input)
self.assertEqual(0, result.returncode, result.stderr)
self.assertTrue(self.generated("base/my_functions").is_file())
self.assertEqual(2, result.returncode)
self.assertIn(f"无法识别的参数: {self.input}", result.stderr)
self.assertFalse(self.generated("base/my_functions").exists())
def test_help_marks_legacy_input_as_deprecated(self):
def test_help_only_documents_named_input_options(self):
result = self.run_raw_cli("--help")
self.assertEqual(0, result.returncode, result.stderr)
self.assertIn("--file INPUT_FILE", result.stdout)
self.assertIn("--dir INPUT_DIR", result.stdout)
self.assertIn("已废弃,请使用 --file", result.stdout)
self.assertNotIn("positional arguments:", result.stdout)
self.assertNotIn("已废弃", result.stdout)
def test_invalid_format_error_is_fully_chinese(self):
result = self.run_raw_cli("--file", self.input, "--format", "xml")
@@ -192,7 +194,6 @@ class DocGenCliTest(unittest.TestCase):
cases = (
(),
("--file", self.input, "--dir", self.root),
(self.input, "--file", self.input),
)
for args in cases:
with self.subTest(args=args):
@@ -200,7 +201,7 @@ class DocGenCliTest(unittest.TestCase):
self.assertEqual(2, result.returncode)
self.assertIn(
"必须且只能指定一种输入方式:INPUT_FILE、--file 或 --dir",
"必须且只能指定一种输入方式:--file 或 --dir",
result.stderr,
)
@@ -399,6 +400,49 @@ class DocGenCliTest(unittest.TestCase):
self.assertEqual(result.returncode, 0, result.stderr)
self.assertTrue(output.is_file())
def test_root_writes_under_explicit_project_root(self):
output_root = self.root / "explicit-root"
result = self.run_cli("--root", str(output_root))
output = (
output_root
/ "skills"
/ "tsl-api-reference"
/ "references"
/ "codegen"
/ "project"
/ "base"
/ "my_functions.md"
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertTrue(output.is_file())
self.assertFalse(self.generated("base/my_functions").exists())
def test_relative_root_is_resolved_from_current_working_directory(self):
result = self.run_cli("--root", "explicit-root")
output = (
self.root
/ "explicit-root"
/ "skills"
/ "tsl-api-reference"
/ "references"
/ "codegen"
/ "project"
/ "base"
/ "my_functions.md"
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertTrue(output.is_file())
def test_help_documents_root_option(self):
result = self.run_raw_cli("--help")
self.assertEqual(0, result.returncode, result.stderr)
self.assertIn("--root ROOT_DIR", result.stdout)
self.assertIn("包含 skills 目录的项目根目录", result.stdout)
def test_output_option_is_rejected(self):
result = self.run_cli("--output", str(self.root / "out.md"))
self.assertNotEqual(result.returncode, 0)
@@ -498,12 +542,19 @@ class DocGenCliTest(unittest.TestCase):
output.write_text("原内容\n", encoding="utf-8")
bin_dir = self.root / "bin"
bin_dir.mkdir()
fake_npx = bin_dir / "npx"
fake_npx.write_text(
"#!/bin/sh\necho formatter-failed >&2\nexit 9\n",
encoding="utf-8",
)
fake_npx.chmod(0o755)
if os.name == "nt":
fake_npx = bin_dir / "npx.cmd"
fake_npx.write_text(
"@echo off\r\necho formatter-failed 1>&2\r\nexit /b 9\r\n",
encoding="utf-8",
)
else:
fake_npx = bin_dir / "npx"
fake_npx.write_text(
"#!/bin/sh\necho formatter-failed >&2\nexit 9\n",
encoding="utf-8",
)
fake_npx.chmod(0o755)
env = os.environ.copy()
env["PATH"] = str(bin_dir)
@@ -884,6 +935,46 @@ class DocGenCliTest(unittest.TestCase):
self.assertIn("范例02:多行输出", text)
self.assertIn("// 输出:\n// first\n// second", text)
def test_singular_example_is_rejected_without_overwrite(self):
self.write_input(
{
"module": "项目 / 旧示例格式",
"path": "base/singular_example",
"declarations": [
{
"kind": "function",
"name": "demo",
"signature": "demo()",
"desc": "示例函数。",
"returns": "nil",
"example": "return demo();",
}
],
}
)
self.assert_rejected_without_overwrite(
"base/singular_example", "存在未知字段:example"
)
def test_singular_method_example_is_rejected_without_overwrite(self):
self.write_class_member_input(
{
"kind": "method",
"name": "Run",
"visibility": "public",
"binding": "instance",
"signature": "Run()",
"desc": "运行。",
"example": "return self.Run();",
},
"base/singular_method_example",
)
self.assert_rejected_without_overwrite(
"base/singular_method_example", "存在未知字段:example"
)
def test_top_level_enum_sections_are_plain_text_not_emphasis_headings(self):
self.write_input(
{