一键导入
parsley-coding
How to write Parsley code for tests, examples and documentation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How to write Parsley code for tests, examples and documentation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Build and run a Basil server against a scratch site to observe changes at the real HTTP surface.
Run the project Definition of Done for the current unit of work and integrate it — build, test, verify, docs/changelog, commit, rebase, merge to main, push, and remove the worktree. Use when a piece of work is finished and should land on main. Merge model is direct-to-main (no PR).
Develop and test Basil web applications with HTTP handlers, routing, sessions, authentication, databases, and interactive components (Parts).
| name | parsley-coding |
| description | How to write Parsley code for tests, examples and documentation. |
| updated | "2026-01-11T00:00:00.000Z" |
This skill helps you write and debug Parsley code.
Use this skill when you need to:
Important distinction:
./parsBasil-only features (not available in standalone Parsley):
@params - URL/form parametersimport @basil/http - request, response, route, method (query removed; use @params)import @basil/auth - session, auth, userParsley features work everywhere:
import @std/* modules (math, valid, schema, etc.)When writing code, consider:
./pars to test pure Parsley code@basil/* importsdocs/parsley/CHEATSHEET.md to learn Parsley code especially common mistakespars to test and validate Parsley code snippetsif and for are expressions that return valuesfn) return everything by default, so do not need return statement@ constructorsimport @std/….pars file-ending(see docs/parsley/CHEATSHEET.md for more Major Gotchas (Common Mistakes))
export Table = fn({rows, hidden, ...props}){
<table ...props>
<thead>
<tr>
for (k,_ in rows[0]){
if (k not in hidden)
<th class={"th-"+k}>k.toTitle()</th>
}
</tr>
</thead>
<tbody>
for (row in rows){
<tr>
for (k,v in row){
if (k not in hidden)
<td class={"td-"+k}>v</td>
}
</tr>
}
</tbody>
</table>
}
// Standard library (works everywhere)
let {floor, ceil} = import @std/math
let {table} = import @std/table
// Basil context (handler-only)
let {request, response, route, method} = import @basil/http // Basil-only
let {session, auth, user} = import @basil/auth // Basil-only
// Local modules (works everywhere)
let {People} = import @~/schema/birthdays.pars
for returns an array of values, making it more like map and filterlet doubled = for (n in [1,2,3]) { n * 2 } // [2, 4, 6]
// Filter pattern - if returns null, element is omitted from result
let evens = for (n in [1,2,3,4]) {
if (n % 2 == 0) { n }
}
// evens => [2, 4]
if age >= 18 { "adult" }
if (age >= 18) { "adult" }
let status = if (age >= 18) "adult" else "minor"
// ✅ CORRECT
let path = @./config.json // Relative to current file
let rootPath = @~/components/nav // Relative to project root (Basil)
// ❌ WRONG
let path = "./config.json" // This is just a string
// Other literals
let url = @https://example.com
let date = @2024-11-29
let time = @14:30
let duration = @1d
// ❌ WRONG (JavaScript arrow functions)
arr.map(x => x * 2)
// ✅ CORRECT - Use fn() { } syntax
arr.map(fn(x) { x * 2 })
arr.filter(fn(x) { x > 0 })
// Named functions
let double = fn(x) { x * 2 }
<h3>Welcome to Parsley</h3> // ❌ WRONG - bare text in tags
<h3>"Welcome to Parsley"</h3> // ✅ CORRECT - strings need quotes
<h3>`Welcome to {name}`</h3> // Template literal style also works
// ✅ CORRECT - no quotes needed for simple identifiers
<div class=container id=main disabled=true>
<button type=submit disabled={isDisabled}>
// ✅ ALSO CORRECT - quotes when you need them
<div class="user-profile" id={userId}>
<a href="/about">
// Use quotes for:
// - Multi-word values: class="nav item"
// - Values with special chars: onclick="alert('hi')"
// - String literals vs expressions
// Tag attributes have THREE forms:
// 1. Double-quoted strings - literal, no interpolation
<button onclick="alert('hello')">
<a href="/about">
// 2. Single-quoted strings - RAW, for embedding JavaScript
<button onclick='fetch("/api/items", {method: "POST"})'>
// ^ Double quotes and braces stay literal - perfect for JS!
// Use @{} for dynamic values:
<button onclick='fetch("/api/items/@{myId}", {method: "DELETE"})'>
// 3. Expression braces - Parsley code
<div class={`user-{id}`}> // Template string for dynamic class
<button disabled={!isValid}> // Boolean expression
<img width={width} height={height}>
// ❌ WRONG - interpolation in quoted strings
<div class="user-{id}"> // {id} is literal text
// ✅ CORRECT - use expression braces with template string
<div class={`user-{id}`}>
// Double quotes: Plain strings (no interpolation)
let msg = "Hello {name}" // literal braces, no interpolation
// Backticks: Template literals (JavaScript style, with interpolation)
let msg = `Hello {name}`
// Single quotes: RAW strings - {braces} stay literal
let js = 'fetch("/api/items", {method: "POST"})'
let regex = '\d+\.\d+' // Backslashes stay literal
// Use @{} for interpolation inside raw strings
let id = 42
let js = 'fetch("/api/items/@{id}", {method: "DELETE"})' // id interpolated
// Perfect for onclick handlers with dynamic values:
let myId = 5
<button onclick='fetch("/api/items/@{myId}", {method: "DELETE"})'/>
// Static JS (no interpolation needed):
<button onclick='document.getElementById("output").innerHTML = "done"'/>
// Escape @ with \@ if you need a literal @
'email: user\@domain.com' // literal @
// Singleton tags (HTML void elements) MUST self-close:
// ❌ WRONG
<br>
<img src="photo.jpg">
<input type="text">
<Part src={@./foo.part}>
// ✅ CORRECT - singleton tags need />
<br/>
<img src="photo.jpg"/>
<input type="text"/>
<Part src={@./foo.part}/>
// Paired tags can be empty (no /> needed):
// ✅ CORRECT
<div></div>
<script></script>
<button></button>
// Dictionaries iterate in insertion order by default
for (k, v in {b: 2, a: 1}) { k } // ["b", "a"]
// To iterate in sorted key order, extract and sort keys first
let d = {b: 2, a: 1}
let sortedKeys = (for (k, _ in d) { k }).sort()
for (k in sortedKeys) { d[k] } // values in key order: [1, 2]
[1,2,3].length() // => 3
"ABC".length() // => 3
123.type() // => "integer"
[1,2,3].type() // => "array"
"hello".type() // => "string"
@1968-11-21.type() // => "datetime"
$5.type() // => "money"
@now.type() // => "datetime"
// Export functions/values to make them available to importers
export myFunc = fn(x) { x * 2 }
export myVar = 42
// Each export must be declared separately
// (no "export {a, b}" shorthand)
// let without export is file-local (private)
let private = 123 // Not available to importers
export public = 456 // Available to importers
// Default export
export default = fn(props) { <div>props.text</div> }
// Works everywhere (standalone pars and Basil handlers)
import @std/mdDoc // Markdown (mdDoc pseudo-type)
import @std/table // Table DSL
import @std/math // Math functions
import @std/valid // Validation functions
import @std/schema // Schema validation
import @std/id // ID generation (UUID, ULID, etc)
import @std/dev // Dev tools
// Requires Basil server (not available in standalone pars)
import @std/api // API helpers (redirect, notFound, etc)
import @std/html // HTML components (Page, Button, etc.)
// ❌ DEPRECATED/REMOVED - don't use these:
// @std/markdown - removed, use @std/mdDoc
// now() builtin - removed, use @now
ONLY available in Basil handlers, NOT in standalone pars:
import @basil/http // request, response, route, method
import @basil/auth // session, auth, user
// Works everywhere
@now // Current datetime
@now.year // 2026
@now.format() // "January 11, 2026" (human-readable)
@env.HOME // Environment variables
@env.PATH
// Basil-only (ONLY in handlers)
@params // URL/form parameters
@params.id // Individual parameter
To test Parsley code locally:
./pars # Start interactive REPL
./pars script.pars # Execute a script
./pars -pp page.pars # Pretty-print HTML output
./pars -x script.pars # Allow unrestricted script execution
./pars --no-write script.pars # Deny file writes
./pars --restrict-read=/etc script.pars # Deny reads from path
NOTE: that .pars converts values to their default string representation and concatenates them:
[1,2,3] -> 123["a","b","c"] - > abc``s = fn(){"Sam" "Was" "Here"}; s() -> SamWasHereWhen writing tests and examples:
pkg/parsley/tests/*.pars or *_test.goexamples/*/handlers/*.pars./pars to test snippets before documenting./basil --dev and test in browserFor comprehensive guide to:
@schema{}@query()@insert()@update()@delete()@transaction{}See the Query DSL SKILL:
.../parsley-query-dsl/SKILL.mdFor comprehensive guides, see:
docs/parsley/CHEATSHEET.md and docs/parsley/reference.mddocs/basil/reference.md - Full language reference with all operators and builtinspars before using in documentation