Repository navigation
Mark exported classes in a static block and identify by the mark alone - #6617
Draft
backspace wants to merge 1 commit into
Conversation
The provenance plugin now visits export declarations and gives each class a module declares and exports a static block that marks it, with no shared helper and no marks on functions or other values. The host build applies it to base modules through babel.config.cjs instead of the bundled-base Vite plugin. Loader.identify reads the mark and nothing else. The loader puts the module's canonical identifier on import.meta for the mark to use, and marks what transpilation did not (shim exports, and modules transpiled before marks existed) instead of keeping its own identity map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015idm3FXE4bujPmH1obGWFG
Contributor
This branch has not been deployed
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.
Stacked on #6614. This PR applies the review feedback on #6614, so the two can be compared before one is chosen.
Background and Goal
#6614 marks each exported class or function with the module that declares it. A helper function is appended to every module, and the loader still keeps its own identity map. Review feedback asked for less: mark only exported classes, make
Loader.identifyread only the mark, use Babel's visitor pattern, and use the host's existing Babel config instead of the bundled-base Vite plugin. This PR makes those changes.Where to start
moduleProvenancePlugininruntime-common/loader-plugin.ts. It visitsExportNamedDeclarationandExportDefaultDeclaration, and puts a static block first in each exported class that the module declares.Loaderinruntime-common/loader.ts.identifyis nowmoduleProvenanceOf(value).markModuleExportsreplacescaptureIdentitiesOfModuleExports.host/babel.config.cjs. Anoverridesentry applies the plugin topackages/basefiles, with a literal@cardstack/base/<name>identifier.Feedback, and what this PR does
identifyshould only use the provenance markLoader.identifyreturnsmoduleProvenanceOf(value). The instanceidentifymethod and the per-loader identity map are removed.bundled-base-scoped-css.mjshandles only scoped CSS again.ExportNamedDeclarationin the visitorProgramexit pass.identifycallers pass card or field classes.Object.definePropertycall). There is no helper, so a module with no exported class gets nothing. A global would make every marked module depend on code that must evaluate before it, which is the module-ordering risk seen in CS-13655.captureIdentitiesOfModuleExportsshould not be neededKey decisions and non-obvious mechanics
The mark has to be in canonical form. A code ref stores the virtual-network form of a module identifier (for example
@cardstack/base/card-api), not the URL.identifyno longer has a loader to convert a URL, so the loader addsimport.meta.moduleIdentifier(already in canonical form) to each module it evaluates, and the mark reads it. A module evaluated without a loader falls back toimport.meta.url.Why the loader still marks exports.
markModuleExportsdoes two jobs:Loader.loadersforLoader.getLoaderFor, which card-api, operations and patch-fields still use.A value that already has a mark keeps it. So the mark from transpilation always wins, and
identifyhas one source.The mark is in a static block, first in the class body. So the mark is set when the class is created, before any other static code runs. That includes cases where a circular import reads the class before its module finishes evaluating. A subclass doesn't run its parent's static block, and
moduleProvenanceOfreads only an own property, so a subclass never takes its parent's mark.One behaviour change for shims. Before, each loader kept its own identity map. Now a shimmed class gets one mark for all loaders, and the first shim wins. Production shims (base, externals, host tools) always use the same identifier, so only a test that shims one class object under two different URLs would see a difference.
🤖 Generated with Claude Code
https://claude.ai/code/session_015idm3FXE4bujPmH1obGWFG