feat(modules): ESM-style import/export syntax with compile-time resolution (#43)#44
Open
attitude wants to merge 1 commit into
Open
feat(modules): ESM-style import/export syntax with compile-time resolution (#43)#44attitude wants to merge 1 commit into
attitude wants to merge 1 commit into
Conversation
…ution (#43) A plugin built entirely on the released seams — no parser or compiler changes: - ImportExportRecognizer (SyntaxRecognizer): claims import/export statements, leaves the runtime import(...) call form unclaimed - ImportExportVisitor (NodeVisitor): resolves specifiers at compile time against the source file, bakes root-relative module ids, lowers to plain PHP; exports collected for the module return - Modules\load()/module()/resolver(): plain-require registry with circular-import detection; lazy read-only Module wrapper for namespace imports - Modules\compile(): wires recognizer + visitor and appends the module's return ['Name' => $value, ...] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #43 — ESM-style
import/exportstatements as a syntax plugin built entirely on the released seams (#42SyntaxRecognizer, #30NodeVisitor). No parser or compiler changes — the diff is new files undersrc/modules/+ two composer autoload lines.What's in the box
ImportExportRecognizerSyntaxRecognizer(#42)ImportExportVisitor(resolution + lowering)NodeVisitor(#30)Modules\load()/module()/resolver()+ lazyModuleModules\compile()facadeCompilerSupported syntax
import { TodoItem, TodoList } from './components'; // named import { Store as TodoStore } from './store'; // aliased import * as todo from './todo'; // namespace (lazy Module wrapper) import viteLogo from '#/assets/vite.svg'; // default / asset import { Suspense } from 'attitude/phpx-server'; // bare specifier export $TodoItem; // named export export default $Root; // default export export { Store } from './store'; // re-exportKey design decisions (per the issue)
.//../against the importing file,#/via the alias map, candidatesx→x.phpx→x.php. "Module not found" is a compile-time error withfile:line. Baked output is a canonical root-relative module id:\Attitude\PHPX\Modules\load('src/components.phpx')— the runtime never guesses paths, which kills thedebug_backtrace()and cache-dir failure modes of the runtime prototype by construction.return ['Name' => $value, ...];, so compiled modules stay consumable by plainrequire. The only class is the lazy read-onlyModuleArrayAccess wrapper forimport * as.require, neverrequire_once— the registry is the only dedup layer, so the "returnstrueinstead of exports" failure mode is gone. Circular imports are detected mid-cycle and reported with the full chain:Circular import: a → b → a.import(...)stays unclaimed — the claims lookahead only takesimportfollowed by{,*, or an identifier +from, so the runtime prototype coexists during migration.$Suspense = \Attitude\PHPX\Server\Suspense(...);instead ofuse function. Ause functionimport would not produce the$Suspensevariable that<Suspense>compiles to (formatElementTypeemits capitalized tags as variables); the FCC assignment keeps the same zero-runtime-cost intent and keeps components closures.List,Switch, …) are handled by word-matching identifiers the same way the core parser matches JSX names — PHP tokenizes them as keywords, notT_STRING.Tests
616 passing (575 pre-existing + 41 new): byte-for-byte pinned lowered output for every syntax form, resolution/nesting/duplicate-export error paths with
file:line, loader semantics (evaluate-once, cycle chain, lazyModule, read-only exports), and a compile → write →load()integration round-trip.Scope / non-goals (as proposed)
No live bindings, no side-effect-only
import './x';, no dynamicimport()expression (the prototype covers it). Asset imports beyond the default id-string handler are pluggable via$assetHandler.Closes #43
🤖 Generated with Claude Code