Repository navigation
Keep the declarations of TypeScript namespaces out of the module scope - #130
Merged
Merged
Conversation
SCOPE_VISITOR gave namespace bodies (TSModuleBlock, also used by
`declare module` and `declare global`) no scope of their own, so their
declarations landed in the scope around them. Where ecij sees
namespaces, that is the modules that plain Rolldown and tsdown hand to
its transform and the modules it reads from disk, this had two effects:
- A namespace's `const css` shadowed the ecij import in the whole
module, so none of its templates were extracted, without a warning,
and their runtime `css` calls threw.
- A namespace's bindings leaked into the module scope, overriding the
module's own bindings of the same name, so a module-level `${accent}`
could resolve to the namespace's `accent`.
Namespace bodies now have a scope, which `var` declarations hoist to,
as a namespace compiles to a function.
A template that interpolates an export of another block of the same
namespace no longer resolves it. Rolldown's TypeScript transform leaves
such references unresolved too, and Vite, which compiles namespaces
before ecij transforms the module, already skipped these templates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
The logs of a plain Rolldown build hold absolute paths, which buildWithPlugin normalizes for the Vite builds, so its normalization moves to a normalizeLog helper that both use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cope Namespaces are not supported: their scope only stops their declarations from overriding the module's bindings, ecij's css included. The test no longer covers the templates inside namespaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nstepien
marked this pull request as ready for review
November 7, 2026 05:21
nstepien
added a commit
that referenced
this pull request
Nov 7, 2026
Main reworked the import handling underneath this branch: imports read from the top-level statements (#127), parses cached while pending (#128), imports as bindings of the module-level scope with default imports through `*default*` (#131), values resolved without promises (#133), and TypeScript namespaces given a scope of their own (#130). src/index.ts was taken from main and this branch's features ported onto it: - Namespace imports are `NamespaceImport` bindings, and member chains (`ns.a.b`) resolve through the exports of each namespace. - Exports are `moduleExports` (local, re-export, namespace) plus `exportStarSources`, read from the top-level statements, and `resolveExport` follows them with ESM precedence and ambiguity. Exporting an imported binding re-exports it, as in the spec. Exported functions, classes, enums, and destructured bindings are recorded, so that they take precedence over `export *`. - Type-only imports and exports are skipped through `importKind` and `exportKind`, as the module record is no longer read. - Main's `isCssTag`, `bindingValues`, and `MaybePromise` resolution are kept. An import resolves through `resolveExport` and imports the stylesheet of the module that declares the value. Main's decisions win where they differ from this branch: - TypeScript namespaces are not supported: the namespace-name and `import x =` bindings are dropped, along with the typed fixture's namespace cases, as main's namespace test covers containment. Enums still shadow. - Templates exported by default get no `default` name, so their class names hash like main's. Main's import-forms test now extracts the namespace member and the re-export it skipped. Two fixture cases cover the export of a namespace import and a member of a local object. Co-Authored-By: Claude Opus 5.5 <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.
TypeScript namespaces are discouraged and stay unsupported, but they could break the code around them.
SCOPE_VISITORgave namespace bodies no scope of their own, so their declarations landed in the module scope:cssturned off extraction for the whole module. Aconst cssin a namespace replaced the ecij import, so none of the module's templates were extracted, nothing warned, and theircsscalls threw at runtime.${accent}got the namespace'sredinstead of the module'sblue.A namespace body now gets its own scope, so its declarations stay inside it.
TSModuleBlockis also the body ofdeclare moduleanddeclare globalblocks, and a namespace compiles to a function, so the scope is function-scoped forvardeclarations. The comment above the callback says that namespaces are otherwise not supported.ecij sees namespaces in two places: the modules that plain Rolldown and tsdown pass to its transform, as they compile TypeScript afterwards, and the modules it reads from disk to resolve imported values, with any bundler, Vite included. Vite compiles namespaces to functions before ecij transforms a module.
Templates inside a namespace are now resolved in its scope, but that is a side effect, which the test does not cover. A template that used the export of another block of the same namespace no longer resolves, as main only resolved it through the leak, and Rolldown's own TypeScript transform does not resolve it either.
Test
The new test builds
test/fixtures/namespace-scoping.input.tswith plain Rolldown. A namespace declares anaccentand acss, and the module-level template is extracted with the module'saccent. On main, nothing is extracted, and without the namespace'scss, the template gets the namespace'sred. The fixture's namespace needs@ts-expect-error, as the test tsconfig enableserasableSyntaxOnly. The coverage is unchanged, with the new callback covered.🤖 Generated with Claude Code