Repository navigation
Resolve the members of namespace imports - #135
Merged
Merged
Conversation
Interpolations can read the exports of a module through a namespace
import, like `${tokens.color}`, including class names, whose stylesheet
is imported first, as with named imports. Until now, a namespace import
was an unknown binding, so `${ns.x}` skipped its template.
A namespace import is now a binding of the module-level scope, like
other imports, and resolves to the parse of its module, which stands
for the namespace. A value is therefore either a string or a namespace,
and only strings are interpolated. A member of a namespace resolves to
the export of that name, so a named import is resolved as the member
of the namespace of its module. Namespaces resolve through the same
bindings as other values: exported again, imported by name, aliased by
a `const`, and nested, like `${ns.inner.x}`.
Members are resolved by name only, so computed members, like
`ns['x']`, are still skipped. Warnings name the whole chain of members
that does not resolve, like `ns.x.length`, and member expressions now
get `UNRESOLVED_INTERPOLATION` instead of `COMPLEX_INTERPOLATION`.
Resolution stays synchronous unless an import has to be loaded first.
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.
|
Returning a member without a promise once its namespace is resolved took a .then branch for both the namespace and the value, and was not measurably faster, even with 16,000 member reads per build. resolveMember now awaits them, like resolveImport on main, and imports the stylesheet itself, as importStylesheet was left with one caller. AGENTS.md and the resolveValue comment now say that a value is only returned as a promise when it is read through an import or a member. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
resolveImport only chose between the namespace of a module and one of its members, so resolveBinding now does that itself. resolveBinding stays synchronous: as an async function, it made the build of 400 components importing 40 tokens each by name about 3% slower, in all 10 paired runs, with no difference at 40 components or with a namespace import. 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.
Interpolations can now read the exports of a module through a namespace import:
Until now, a namespace import was an unknown binding, and
${tokens.color}skipped its whole template with aCOMPLEX_INTERPOLATIONwarning.How it works
import * as ns from 'mod'bindsnsin the module-level scope as aNamespaceImport, like named and default imports.StaticValue), and only strings are interpolated, so${ns}is skipped instead of interpolating[object Object].resolveMemberreplacesresolveImportand, like it, isasync: it waits for the namespace, resolves its member to the export of that name, and imports the module's stylesheet first when the member is a string, as it may be one of its class names.resolveBindingnow resolves a named or default import as the member of the namespace of its module.export { ns }), imported by name, aliased by aconst, or nested (${ns.inner.x}).resolveBindingstays synchronous, as returning every binding as a promise is measurably slower with many named imports.Warnings
could not resolve "ns.x.length". Chains of members of an identifier now getUNRESOLVED_INTERPOLATIONinstead ofCOMPLEX_INTERPOLATION, including the members of local objects, liketheme.color, which are still not resolved.ns['x'], are not resolved and still getCOMPLEX_INTERPOLATION.Not included
export * as ns from 'mod',export { x } from 'mod', andexport *. A member that a module re-exports, likens.reexported, is skipped.ns['string name']is the only way to read an export named by a string through a namespace. Named imports of such exports (import { 'string name' as name }) resolve.csstag itself must still be a named import, as thecodefilter only matches named imports fromecij:ecij.cssfromimport * as ecij from 'ecij'is not extracted.Tests
helper.default.resolve the members of namespace imports, covers values and class names, with the imported stylesheet first in the CSS, a namespace exported again and read as a member or imported by name,constaliases of a namespace and of a member, and the forms that are skipped: a re-exported member, a namespace interpolated as a value, a member of a string, a member of a local object, a computed member, and a parameter shadowing the namespace.TSParameterPropertybranch, with 15 uncovered branches against 16 onmain.tokens.tsupdates the CSS of a module that readstokens.colorthrough a namespace, and of the module that reads its class name through another namespace.Benchmarks
Medians of Vite builds of a synthetic project: N components × 8 templates × 5 tokens from one
tokens.ts, each variant in its own process, with the range over 4 processes. The JS and CSS of the named-import project are identical onmainand on this branch, and the namespace project's CSS only differs in its class name hashes.main, named importsA namespace import resolves one binding per module instead of one per token. Returning members without a promise once their namespace is resolved was not measurably faster: in another run at 400 components, the namespace project built in 128.9–130.0 ms that way, leaving out one process at 139.2 ms, and in 128.8–132.2 ms with
async. MakingresolveBindingasync too made the named-import project about 3% slower at 400 components, with a median of 4.3 ms over 10 paired runs that were all slower, and made no difference at 40 components or with a namespace import. Benchmarking both plugins in the same process measured this branch 1.5–2 ms slower at 40 components, but not at 400, and separate processes do not reproduce it.🤖 Generated with Claude Code