Repository navigation
feat: import shared modules and module-level values - #69
Merged
Merged
Conversation
A component reads values from relative .ts modules and its module's top-level consts, each through a read-only binding of its own (import, moduleConst). The module records its value imports and the constants its components read, and a constant's value reads only the module's other constants, its imports and the pure globals (ADR-0070). The resolver reads a shared module and follows its relative imports through every .ts module they reach. compile() copies each into the output trees beside a component's files, unchanged, and lists it in CompileResult.dependencies. A shared module that imports a framework or unframework, itself or through another .ts, is UF1204, with its case diagnostics/framework-in-shared-module, and the component compiles for no target. A value import of a module the resolver cannot read is UF1202. The harness keeps a path two sources produce once when the contents are the same, and the analyser's corpus test reads a case's .ts value modules. UF2033's description says where a key lives instead of naming M5. Co-authored-by: multica-agent <github@multica.ai>
… of modules A module constant's value is checked as setup code is: JSX is UF3012, chance, the clock and the locale UF3019, import.meta and import() UF1002, and a write of a module's value UF2011, or a change in place UF2004. A read of a constant declared after it, outside a function, is UF2023. A shared module's imports include import() with a literal specifier and import x = require(), so UF1204 sees a framework loaded either way, and the module a lazy import names is copied too. The resolver reads each module once, and tests pin the unframework arm of UF1204. The unplugin joins only components' files, so an Angular module no longer holds the copies. A copy at a component's output file is UF1104 on that target, at the import that reaches it. Co-authored-by: multica-agent <github@multica.ai>
…om outputs A module constant is a function, whose body runs once called, or any other value, all of which runs as the module loads. Nothing in it changes a value in place, through as, !, patterns, aliases and computed calls too (UF2004), and a function writes only its own locals (UF2011). this, function expressions and computed calls are UF1002. As the module loads, a value reads no later constant, itself or through the functions it calls (UF2023), and a type it names reads nothing. Chance, the clock and the locale in a function mark it, and a template, a getter or an initial value that calls it reports UF3019, as setup does; client code may call it. A copy named like a component's output file without its extension, the module's own or an imported child's, is UF1104 for every target. A shared module's import() of a template without holes is followed, and any other import() or import.meta.glob is UF1202. Co-authored-by: multica-agent <github@multica.ai>
A module constant changes in place only a value it builds itself: a
literal, a new, a copy, or a function's local that only ever holds one,
a reduce accumulator too. So Object.freeze({ ... }) and a helper that
pushes to its own array are plain.
A global only client code may read, in a module function, waits for its
call: a template, a getter or an initial value that calls it reports
UF2014, as setup does. An arrow a value holds runs once called, so it
may read a later constant. A local type a constant writes is declared
with it. A test pins UF1104's kebab-case name.
Co-authored-by: multica-agent <github@multica.ai>
…d-modules-uxf-457
A parameter holds what its caller gives, so only a reduce accumulator counts as fresh, and a copying method gives a fresh value only on an array. Defaulting a parameter, then filling it, no longer changes a module's value silently (UF2004). An arrow a value holds runs where a value calls it through a member as the module loads (O.m()), so what it reads counts (UF2023). A module function a template, a getter or an initial value reads, or a value that holds one, is judged as a call (UF2014, UF3019), and so is one passed to a call as the module loads. Co-authored-by: multica-agent <github@multica.ai>
A copying method's result is fresh whatever its receiver, but an object literal, whose own method may give what it holds: filter, then sort, on a parameter's member or a module table no longer reads as a change of it. Freezing a value changes nothing anyone reads. A value passed to a call as the module loads counts as called only when it is a function constant, and a call whose result cannot hold it (Object.keys, JSON.stringify, String) only reads it: a table of functions passed to Object.keys neither runs nor carries them. Co-authored-by: multica-agent <github@multica.ai>
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.
What changed
Components can read values from shared
.tsmodules and from their own module's top-levelconsts (ADR-0070)..tsmodule is animportbinding. A top-levelconstis amoduleConstbinding. Both are read-only: code that writes one is UF2011, and code that changes one in place is UF2004. The module recordsmoduleImportsand theconstantsits components read.new, a copy (filter,map,slice, … of anything but an object literal), or a function's local that only ever holds one. A parameter never counts, except areduceaccumulator. SoObject.freeze(…), "filter, then sort", and a helper that pushes to its own array are fine. Writes,delete, mutating methods andObject.assignon anything else are UF2004, and a function writes only its own locals (UF2011).O.m()). One that a value holds runs once called, and a table of functions passed toObject.keysis only read. A local type a constant names is declared with it.this,import.meta,import(), function expressions, classes and computed calls are UF1002.XS.map(fmt),O.fmt()), as UF3019 or UF2014, as setup does. Client code may call it.let, a destructured or exportedconst, and aconstthat declares two names are UF1002.compile().Resolver.modulereads a shared module and follows its relative imports through every.tsmodule they reach, includingimport()andimport x = require().compile()copies each module, unchanged, into the output trees beside the component files. It also lists each one inCompileResult.dependencies.unframework, directly or through another.ts, is UF1204, and its component compiles for no target. The code's case isdiagnostics/framework-in-shared-module.caseOutputskeeps a path that two sources produce once, when the contents are the same. The analyser's corpus test reads a case's.tsvalue modules.Framework output: no component's output changes on any target. Each output tree gains a copy of the
.tsmodules its source reaches. Until each target's composables lane lands, a target still reports UF1002 where a component reads animportormoduleConstbinding, and no corpus case does that yet.Checklist
feat(web): sell the next framework on the homepage.pnpm format:check,pnpm lint,pnpm check-typesandpnpm testpass. The first three pass. The tests of every touched package pass, and so does the full integration suite on all seven targets, plus the L1 and L2 canaries. The rootpnpm testwas not run as one command.pnpm changeset(see.changeset/README.md). Not needed: every package is private.pnpm build, thenpnpm --filter web lighthouse, still scores at least 0.95 in every category. Not needed: no homepage change.