Tools

Repository Author Commit message Committed SHA  
 
vite-plugin by dg dropped support for Node.js 20, requires Node.js 22+ (BC break)

Node.js 20 reached end of life in April 2026. The CI matrix now runs
22 and 24, package.json declares engines >= 22, and the runtime guard
around fs.globSync is gone, so glob entries work on every supported
version. Also bumped actions/setup-node from v4 to v7.

vite-plugin by dg readme: rewrote as a full feature overview

Documents every plugin option (entry, refresh, host, appUrl, infoFile),
the Vite defaults the plugin injects, dev-mode detection via the info file,
and the CORS/Docker/proxy/network recipes.

vite-plugin by dg released version 1.1.0
vite-plugin by dg fix: guard glob entries on Node < 22 and de-duplicate exit cleanup

Review follow-ups:
- fs.globSync exists only on Node 22+, but Vite 6 supports Node 18+;
a glob entry on an older runtime threw a cryptic 'not a function'.
Throw a clear error from the glob branch instead.
- The signal handler removed info files and then called process.exit(0),
which re-ran the same cleanup via the 'exit' handler. Let signals just
exit; the single 'exit' handler owns cleanup.

coding-standard by dg output: added summary telling whether any file was actually modified

The sniffer's "No violations were found" was printed last and read as the
verdict of the whole run, even when PHP CS Fixer had just rewritten files.
Neither engine knows what the other did, so run.php now hashes the file list
before and after the run and reports the union in a Summary block. Each
engine's output is also delimited by a header, and a preset missing from one
of the two trees announces the skip instead of doing nothing silently.

coding-standard by dg fixed import types being mixed together when reordering

The stock ordered_imports fixer keeps the original statement prefixes in
place and pours the sorted names back into them. Since Nette turns off
single_import_per_statement, a comma-separated `use const` sitting above a
`use function` came back as `use function c, d, A;`, and a class statement
could end up holding a function - valid syntax that silently changes what
the names resolve to.

Nette/ordered_imports replaces it and regenerates the whole block instead,
so a name can never change its import type. It keeps each type's shape, the
number of statements and how many names each held, so it only reorders and
never merges or splits what the author wrote. A comment anywhere in the
block makes the block untouchable, because a rewrite would drop it and
reordering around it would reattach it to an import it never described.
php80.php enforces `ordered_imports => false` after merging the project
overrides; re-enabling the stock fixer would run both at priority -30 in
undefined order and corrupt the result again.

OptimizeGlobalCallsSniff now anchors a new block to the last statement it
may follow instead of the last one in the file, so `use function` lands
above an existing `use const`. That matters on its own because the
optimize-fn preset has no fixer tree, so running it skips PHP CS Fixer
entirely and the sniff cannot lean on the fixer to place its output.

coding-standard by dg NoLeadingSlashInGlobalNamespaceFixer: added fork respecting imports

The PhpCsFixerCustomFixers fixer decides only from the preceding token, so in a
file without a namespace declaration it rewrites `new \Foo\Bar` to `new Foo\Bar`
even when `use Foo\Foo;` makes Foo an alias. The name then resolves to
Foo\Foo\Bar and the code breaks at runtime, silently. Single-segment names are
exposed the same way, `use Foo\Exception;` shadows `\Exception`.

The fork collects the class imports of the file and keeps the slash when the
first segment of the name matches one of them.

claude-code by dg Released version 1.2.0
claude-code by dg skills: added seven skills for areas nothing covered

Nette\Security appeared in no skill at all; the same held for sessions,
routing, caching, mail, the component tree and the retired-name lookup.

The scope of each was set by measuring what models actually produce rather
than by the size of the documentation, so they document the traps instead of
teaching the topic and are far shorter than a full guide. Every claim was
verified against the library source at a named tag, most of them by running
the code, and all seven were then re-checked by an independent pass.

- nette-security: the Authenticator contract and its error codes,
SimpleIdentity against the 2.x names, Passwords as a service with instance
methods, logout() keeping the identity, sliding expiration, the Permission
ACL including last-parent-wins, IdentityHandler for stale roles
- nette-components: factories, signals and where they run, why a form submit
is not a signal, what {plink} really is, persistent parameters,
redrawControl against the nonexistent invalidateControl, payload.redirect
during AJAX, dynamic snippets, the argument-free getComponents()
- nette-routing: that route order governs link generation and not only
matching, one-way routes, filter order, canonicalization, and that
cache: true cannot serialize a closure filter
- nette-http: the Sec-Fetch based CSRF protection, that any non-browser
client fails it quietly, sessions through set/get/remove, the proxy trap,
and the SSRF UrlValidator
- nette-caching: Cache::Expire rather than Expiration, remove() rather than
the nonexistent forget(), which storages need a journal, and that a closure
in Cache::Callbacks is a fatal because the dependency is serialized
- nette-mail: setHtmlBody auto-embedding, addEmbeddedFile returning a
MimePart that must not be concatenated, SmtpMailer having no default
credentials, and the 4.2 additions
- nette-upgrading: retired name to current name across all packages, sorted
by whether the old name still resolves; the aliases sit in an if (false)
branch, so the IDE and PHPStan flag them while nothing fires at runtime

claude-code by dg skills: documented Latte 3.1 smart HTML attributes

Printing an attribute is the most common thing a template does and 3.1
changed how it behaves, so a model trained on older Latte writes 3.0 code.
Every example here was produced by running it.

Covers the bool, tristate, valued-bool, list, style, data and aria
categories, that the handling applies only when the whole value is one
{...} expression, that a keyed array item counts only when its value is
=== true, and the traps: URL attributes are exempt through the internal
checkUrl filter, and a real filter stringifies the value and defeats the
whole mechanism.

Also prefers class={[...]} over n:class and attribute values over n:attr,
while saying plainly that neither is deprecated and that n:attr remains the
only way to express a dynamic set or name of attributes. Rewriting is not
mechanical: n:class de-duplicates the list and the attribute form does not.

claude-code by dg skills: corrected and extended the existing skills

Every claim was checked against the library source, most of it by executing
the snippet; several skills were then re-checked by an independent pass.

- architecture: the lifecycle had signals inside the action phase; they run
after it and after canonicalization. Added afterRender, shutdown, the
events, and that parent::startup() is enforced
- configuration: only presenters are auto-registered, not components; added
generated factories and accessors (implement:)
- forms: {form} attributes need a comma, Numeric is a non-negative integer
rather than an alias for Integer, corrected the renderer wrapper keys,
Nette\Application\UI\Form belongs in a presenter, onValidate must read
values with getUntrustedValues(), and CSRF is now Sec-Fetch based
- database: array keys in where() are SQL, keywords in fragments must be
uppercase, the empty-array trap, transaction() nesting and its return value
- utils: Callback::closure() and Type::allowsNull() do not exist, corrected
the expected output of isBuiltin(), typeToExtension() and firstLower();
added Process, DateTimeImmutable and the Html escaping contract
- tester: annotations, TestCase, helpers and CLI flags; a test with no
assertion is an error and exit() counts as a pass; @phpVersion with a
three-component version skips the very version it requires
- phpstan: the extension reports errors of its own and forces stricter flags
on, so some errors exist only because it is installed
- assets: a Vite mapper needs an explicit path; documented the plugin options
and that refresh, appUrl and host: network are not in the npm release yet
- neon: spaces are valid indentation too, tabs are only a convention

claude-code by dg Released version 1.1.2
claude-code by dg skills: fixed examples that fail at runtime

Verified against the library sources, several of them by executing the snippet:

- utils: Callback::closure() does not exist in Utils 4, Type::allowsNull() either;
Json::decode() takes forceArrays (plural), Strings::findPrefix() takes one array,
SmartObject needs the @property annotation
- utils: corrected expected outputs of isBuiltin(), typeToExtension() and firstLower()
- latte: only the first filter argument may follow a colon, the rest are comma-separated,
so filter:arg1:arg2 is a CompileException; localDate and the ICU form of number
require a locale; #[TemplateVariable] must be public
- assets: {asset 'app.js'} renders a plain script element, type="module" only for
Vite entry points
- schema: Context::addError() requires the $code argument; array defaults are merged
into the input, which turns a listOf default into a privilege bug on API input
- database: += and -= are ?set key suffixes, not inline SQL operators
- fixed two dead documentation links (database/core, best-practices/phpstan-rules)

phpstan-rules by dg nette/database row type narrowing (table/related/ref/insert)

Narrows return types based on a configurable table-to-entity-class
mapping (nette.database.mapping.tables), which is not yet part of the
nette/database distribution. Kept as the topmost commit so the whole
feature can be excluded or rebased independently.

Includes:
- TableRowTypeResolver shared resolver
- Explorer::table() -> Selection<EntityRow>
- ActiveRow::related() -> GroupedSelection<EntityRow>
- ActiveRow::ref() -> ?EntityRow
- Selection::insert() -> mapped EntityRow

phpstan-rules by dg compatibility with PHPStan 2.2.6

RemoveFailingReturnTypeExtension: DynamicReturnTypeExtensionRegistry is
injected directly, the provider layer was removed from PHPStan.

Tests: PHPStan reverted decimal-int-string inference for regex groups
back to numeric-string.

StringsRegexHelper: matchAllShape() passes wasMatched=maybe; matchAll()
may return an empty list, and PHPStan now infers non-empty-list for yes.

phpstan-rules by dg RemoveFailingReturnTypeExtension: strip |false from glob()

Claude-Session: https://claude.ai/code/session_01YAusXLYHRUMofgyF5LHxqN

phpstan-rules by dg interface @property tag support (type resolution + property.notFound suppression)

PHPStan core ignores @property/@property-read tags on interfaces: the
allowsDynamicProperties() gate skips annotation-based properties there,
so e.g. $asset->url on Nette\Assets\Asset reports property.notFound and
a custom PropertiesClassReflectionExtension is never consulted.

InterfacePropertyTagTypeExtension (ExpressionTypeResolverExtension) gives
such reads the annotated type (narrowed expression types and native
properties win), InterfacePropertyTagIgnoreExtension suppresses the false
property.notFound. Writes are intentionally left unsupported.

Claude-Session: https://claude.ai/code/session_01YAusXLYHRUMofgyF5LHxqN

phpstan-rules by dg suppress false argument.type on Callback::toReflection()

Its @param callable is intentionally strict, but the method escalates the
validity check to a ReflectionException at runtime, so a value that cannot
be statically proven callable (e.g. a [class-string, method] tuple) is valid
by design. New IgnoreErrorExtension matched narrowly to that single method.

phpstan-rules by dg RethrowAbortExceptionRule: don't report a dedicated catch (AbortException)

A catch that explicitly names AbortException (or a subtype) is a deliberate
swallow, e.g. Presenter::run() ending the lifecycle. Only broad catches
(\Throwable, \Exception) that swallow it incidentally are reported now.

phpstan-rules by dg added AGENTS.md & DOCS
coding-standard by dg OptimizeGlobalCallsSniff: fixed bogus constant imports for declared names

Names declared via `const` or enum `case` were detected as global constant
usages, and includedConstants patterns matched case-insensitively, so a class
declaring constants like Php_LogicalOr got a nonsensical grouped `use const`
import generated from the PHP_* pattern. Declared names are now skipped and
constant patterns match case-sensitively (constants are case-sensitive in PHP).

coding-standard by dg AGENTS.md: described the optimize-fn preset
claude-code by dg hooks: project tools are searched upwards from the edited file

lint-latte, lint-neon and lint-js no longer look only in the session
cwd, so monorepos where the session runs above the app directory work
too. The shared findUpwards() helper stops one level above the nearest
.git root. On Windows, lint-latte runs the extensionless PHP script via
PHP_BINARY when no .bat wrapper exists.

mcp-inspector by dg added Tracy Logger WIP
mcp-inspector by dg added built-in toolkits [WIP]
mcp-inspector by dg added built-in DI toolkit
mcp-inspector by dg added inspector
claude-code by dg Released version 1.1.1
claude-code by dg improved skills

fixed YAML frontmatter of three nette-dev skills

vite-plugin by dg feat: add refresh option for full page reloads on template changes

Latte templates and PHP live outside Vite's module graph, so editing them
never triggered HMR. refresh accepts globs (e.g. 'app/**/*.latte'); when a
matching file changes the plugin sends a full-reload to the browser.

Since chokidar v4 dropped glob expansion, each pattern's static base dir
is added to the watcher and matching is done with picomatch (new runtime
dependency).

command-line by dg cs
command-line by dg added Console::getTerminalWidth()

Last synchronization: 2026-08-11 07:02:51