Everything the adaptive question bank can teach and test in Package Management, from foundations through advanced practice. Work through it in order, or start practicing and let the questions find your level.
A. Packages, registries and what a package manager does
The unit of distribution is smaller than people assume — a manifest and whatever files it declares, compressed. Nothing about it is special to any language feature; the registry stores bytes and the client unpacks them, which is why anything can be published and why what ships has to be checked.
One document per name lists every published version with its download location and integrity data, which is what a resolver fetches before it can decide anything. That shape explains why resolution is network-bound at the start and cache-bound afterwards, and why a package with thousands of versions is slow to resolve.
Once published, the contents behind a name and version cannot be rewritten — only marked as discouraged. That immutability is the foundation the whole reproducibility story rests on: a recorded version plus a recorded hash means the same bytes, which would be worthless if publishers could edit in place.
The competing tools speak the same registry protocol, so switching changes how the tree is built on disk and not what can be installed. That separation is why a package published with one works with all of them, and why differences between them are about layout, strictness and speed.
You declare a handful and receive hundreds, because each declaration brings its own. The practical consequence is that most of what you install was chosen by someone else — which is why auditing, licensing and supply-chain questions are about the closure rather than about the manifest.
Resolving decides which versions, fetching retrieves them, and linking arranges them on disk — and a failure looks different in each phase. Knowing which one failed is the first diagnostic step: a resolution error is about ranges, a fetch error about the network, a link error about the filesystem.
A local install is recorded in the project and reproducible by anyone who clones it; a global one is a property of the machine that nothing writes down. That asymmetry is why a build that works on one laptop and not another so often comes down to something installed globally years ago.
B. package.json: identity and metadata
Everything else in the manifest is optional, and those two are required only when publishing — a private project can omit both. That is worth knowing because it separates what the format demands from what a registry demands, which is where most confusion about the file comes from.
The constraints exist because the name becomes part of a URL and a directory path, so it has to survive both. The length limit and the character restrictions are rarely hit deliberately; they surface when a name is generated from something else, such as a directory or a title.
A namespace means a desirable unscoped name already being taken does not block you, and it groups related packages under one owner. It also changes publishing defaults and authentication, which is the part that catches people the first time they publish one.
Declaring an allowlist inverts the default: instead of excluding what you do not want, you name what you do. That is safer because a new file is excluded by default rather than shipped by accident, and it takes precedence over the ignore-file mechanisms.
A small set of files ships regardless of any configuration, and another set never does, which means neither allowlists nor ignore files can change them. Knowing both sets is what makes the tarball predictable, and it is why a license cannot be accidentally omitted.
Setting it makes the publish command refuse outright, which is the mechanism that stops an internal repository or a monorepo root reaching the public registry. It is one line and it protects against the mistake that cannot be undone.
This single field changes how every ambiguous file in the package is interpreted, which makes it one of the highest-leverage lines in the manifest. Adding it to an existing package reinterprets files that were working, so it is a breaking change rather than a configuration tweak.
C. Dependency kinds and who installs what
These travel to everyone who installs your package, so each one is a decision imposed on every consumer — their install size, their audit surface, their resolution conflicts. That is why the bar for this section is higher than for anything else in the manifest.
A consumer installing your package receives nothing from this section, which is what makes it safe to depend on a large test framework or build tool. The distinction only matters at the boundary — within your own repository both sections install identically.
Skipping development dependencies is standard for a deployment image, which means anything the running application actually needs must not be in that section. The failure appears only in the deployed environment, which is the worst place to discover a misfiled dependency.
The declaration says the host application supplies the instance rather than the plugin bundling its own, which is what keeps a single shared copy. It exists for libraries where two copies would not merely waste space but break — anything with module-level state or identity checks.
A failure here does not fail the install, which is useful for platform-specific binaries and dangerous if the code assumes success. Anything declared this way has to be guarded at the point of use, because absence is a normal outcome rather than an error.
Marking a peer as not required suppresses the warning and the automatic installation for a genuinely optional integration. It is the correct way to say this plugin works with or without that host, which the plain declaration cannot express.
Misfiling runs both ways with different symptoms: a build tool in the runtime section inflates every consumer's install, and a runtime library in the development section breaks them entirely. The second is worse and appears only after publishing, which is why the tarball is worth inspecting.
D. Semantic versioning: what a number promises
Three slots with three promises, and the value is entirely in the promise rather than in the arithmetic — the numbers mean nothing unless the publisher honors the convention. That is what makes the scheme a communication mechanism rather than a version-control one.
Below the first major release the convention offers no compatibility promise, and breaking changes conventionally arrive in the second slot. That shifts the meaning of every range operator for such packages, which is the source of the most common surprise in range behavior.
A change is breaking when it alters a documented contract someone depends on, which means an internal refactor is not major however large it is — and a bug fix can be, if code depended on the bug. The judgment is about the contract rather than the diff.
A prerelease of a version precedes the release itself, and the identifiers are compared segment by segment with numeric and alphanumeric segments ordering differently. Assuming a prerelease sorts higher because it appeared later is the mistake, and it produces resolution results that look arbitrary.
Metadata after the plus sign has no effect on ordering at all, so two versions differing only there are the same version for every comparison. It exists to record how something was built, and using it to distinguish releases produces versions that cannot be told apart.
The number is a publisher's claim about compatibility, not an enforced property, so a range is an expression of trust in that claim. The lockfile is what converts trust into evidence — it records what was actually installed rather than what was permitted.
E. Ranges and how a resolver picks a version
The operator permits everything that does not change the leftmost non-zero component, which is why its behavior differs so sharply above and below the first major release. It is the default for a reason: it accepts the changes the convention says are safe and nothing more.
Below the first major release the leftmost non-zero component is the second slot, so the operator becomes far stricter than people expect — narrower than its usual behavior and narrower than the tilde's on a released package. That asymmetry catches almost everyone once.
It permits only the rightmost component to move when all three are given, which makes it the conservative choice for a dependency you do not trust to honor the convention. With a two-part version it behaves differently, which is worth checking rather than assuming.
A bare version pins precisely, and the wildcard forms accept anything published. Both extremes exist and both are usually wrong for an application — the first because a lockfile already provides the pin, the second because it accepts changes nobody reviewed.
Adjacency means every comparator must hold, the double pipe means any set may hold, and the hyphen form is an inclusive interval. Compound ranges are rare in manifests and common in peer declarations, where expressing support for several majors is the whole point.
The resolver picks the highest version the range permits, not the highest that exists, so a manifest can quietly stop receiving updates when a new major is published. That is the intended behavior and it is why a dependency appearing frozen is usually a range question.
A range never matches a prerelease unless the range itself names one, which is why an ordinary operator will not pull in a beta. That exclusion is what makes publishing a prerelease safe, and it is why testing one requires asking for it explicitly.
F. Lockfiles
It records the exact resolved version, the location it came from and a hash of its contents for everything in the closure, which is what makes an install reproducible rather than merely repeatable. All three parts are load-bearing — a version alone does not pin the bytes.
An application commits it because reproducing the exact tree is the point; a library's is never installed by consumers, whose own resolution governs. Publishing a library's lockfile is harmless and pointless, and omitting an application's is how two machines diverge.
The stored hash is verified against what is downloaded, so a tarball substituted after the fact fails rather than installing. That is the mechanism protecting against a compromised mirror or a modified cache, and it works only because published versions are immutable.
When a declared range no longer covers what the lockfile pins, the strict install refuses rather than picking one. That refusal is the feature: guessing would silently make the install non-reproducible, which is the one thing the lockfile exists to prevent.
These files are generated, so a conflict is resolved by regenerating rather than by editing — hand-merging produces a file that parses and describes a tree nobody has ever installed. Take one side, re-run the install, and commit the result.
Each tool writes its own format and none reads another's, so a repository containing two is describing two different trees and honoring whichever tool was run last. That is a real inconsistency rather than untidiness, and it shows up as a build that works locally and not in the pipeline.
G. Installing: npm install versus npm ci, cache and offline
The ordinary install may add packages and rewrite the lockfile, so running it twice on different days can produce different trees from the same manifest. That is appropriate during development and disqualifying in a pipeline, where the tree must be a function of the committed files alone.
It removes the existing tree, installs exactly what the lockfile records, and never writes the file back — so its output depends only on committed state. Those three properties together are what make it the correct command for any automated environment.
It fails outright rather than falling back when the lockfile is missing or inconsistent with the manifest, which converts a silent divergence into a build failure. That strictness is the point, and it is why the first run after a manifest edit fails until the lockfile is updated.
The flags decide whether the manifest is modified and how the range is written, including whether an exact version is recorded rather than a range. Defaults differ between tools and between versions, which is why the resulting manifest line is worth reading rather than assuming.
Caching by integrity hash means the same content is stored once regardless of which package or version requested it, and a repeat install can be served entirely from disk. That is what makes offline installation possible and why cache corruption presents as a hash mismatch.
They fix different failures: a corrupted download is a cache problem, and a wrong or partial tree is a modules problem. Doing both by reflex wastes several minutes on every failure and obscures which one it actually was.
H. node_modules, the resolution algorithm and hoisting
A bare specifier is searched for in the nearest directory and then in each parent up to the root, which is what makes hoisting work and what makes it fragile. The algorithm has no notion of what you declared — it finds whatever is physically present first.
Importing a directory consults its manifest for an entry point and falls back to a conventional filename, which is why a package can be imported by name without naming a file. Modern packages redirect this through the exports mechanism, which takes precedence when present.
The older module system tries a series of extensions when the specifier has none; the newer one requires the exact filename. That single difference accounts for a large share of errors when a working file is converted, and the fix is to write the extension rather than to configure something.
Lifting shared dependencies to the top level means one copy serves many dependents, which saves enormous amounts of disk and time. The cost is that the layout no longer reflects what anything declared, which is what the next two concepts are about.
Importing something you never declared works when hoisting happens to place it where the lookup finds it, and stops working when a different resolver, a different version or a different machine arranges the tree differently. The code is wrong the whole time; only the symptom is intermittent.
When two dependents need incompatible versions the resolver installs a second copy nested beneath one of them, which is why a tree can contain several versions of one library. That is usually correct and invisible — until the library in question has identity or state.
Two copies of a library are two separate modules with separate state, so a value created by one fails an identity check against the other and a singleton exists twice. The symptom is bizarre — a working object rejected as the wrong type — and the cause is in the tree, not the code.
I. Peer dependencies and peer conflicts
A plugin extending a host has to operate on the host's own instance, so bundling a second copy would produce two independent states. Declaring the relationship as a peer is how the plugin says it needs the copy you already have rather than one of its own.
Older clients only warned about an unmet peer and left it to you; current ones install it automatically and refuse when the graph is unsatisfiable. That change means advice written for the older behavior reads as broken, and a conflict that used to be a warning is now a failure.
Two packages demanding incompatible versions of the same peer is a genuine contradiction, not a bug in the resolver — no tree satisfies both. Reading it as a tooling problem leads to flags; reading it as a graph problem leads to the upgrade or the substitution that resolves it.
The flag restores the older ignore-and-warn behavior, which installs a combination the packages themselves say does not work. It is a legitimate temporary unblock and it silences the only warning you would get before the incompatibility shows up at run time.
The durable fix is a published range covering the host version, which means the resolution lives in the dependency rather than in every consumer's flags. Opening that upstream change is slower than the flag and removes the problem for everyone rather than hiding it locally.
J. Overrides, dedupe and aliases
It forces a version onto something deep in the tree that you do not declare directly, which is the only local remedy when a vulnerable transitive dependency has no updated parent. It reaches past the normal resolution rules, which is both the power and the hazard.
Each tool spells the same idea differently, with different syntax and different scoping rules about how far the forcing reaches. Copying a configuration between tools therefore produces something that parses and does not do the same thing, which is worth verifying against the resolved tree.
Forcing a version can violate what the dependent declared it supports, so the combination is untested by definition. It is the right tool for an urgent security fix and the wrong permanent answer, because the real resolution is a parent that accepts the newer version.
Moving duplicated packages upward so one copy satisfies several dependents reduces size and removes some duplicate-instance problems. It can only merge versions that are genuinely compatible, so it is a tidying operation rather than a fix for a real conflict.
Installing a package under a different local name is what allows two majors of one library to coexist deliberately, which is otherwise impossible in a single manifest. It is the practical mechanism for a gradual migration where both versions must run side by side.
K. Scripts and lifecycle hooks
The local binaries directory is prepended for the duration of a script, so a bare command name runs the project's own copy rather than whatever is installed globally. That is what makes a repository's tooling version-controlled rather than a property of the developer's machine.
Any script gains automatic hooks before and after it simply by naming another script with the matching prefix, which means a hook can exist without appearing at the call site. That invisibility is useful and is also why an unexpected build step is often one of these.
Several names fire automatically on install and publish events rather than when invoked, so choosing one of them for an ordinary task produces something that runs at surprising times. Knowing the reserved set is what keeps a script from executing on every consumer's install.
It runs on a local install and before packing, which is precisely what makes a package installed directly from a repository build itself. That dual timing is the reason it is the conventional home for a build step rather than the publish-only hook.
Two script names are invocable without the run keyword and everything else is not, which is a historical convenience rather than a rule. It matters mostly when a new script does not run as expected because the keyword was omitted out of habit.
Arguments before the separator are consumed by the tool and those after it reach the script, which is why a flag intended for a test runner can silently configure the package manager instead. The separator is easy to forget and produces a confusing non-error.
Installing a package can execute code on your machine before you have run anything, which is the most direct path from a compromised package to a compromised developer. Disabling lifecycle scripts is the available mitigation and it breaks packages that genuinely need a build step.
L. bin, npx and executables
The manifest maps a command name to a file and the client creates the link at install time, which is what makes a dependency's command available without a global install. The name is chosen by the package, so two packages can collide on one and the tree decides which wins.
Two things make a file runnable as a command: the interpreter line at the top and the executable permission, and the packing step sets the second. A missing interpreter line produces a syntax error from the shell rather than from the program, which sends people looking in the wrong place.
It runs the project's own copy when one exists and only fetches when none does, which means the same command can behave differently inside and outside a project. That is usually what you want and it makes the version in use worth checking when the behavior surprises you.
When nothing local matches it downloads and executes a package chosen by the name you typed, which makes a typo an arbitrary-code-execution path. The confirmation prompt exists precisely for that, and it is the reason a command copied from an unfamiliar page deserves reading first.
M. Workspaces and monorepos
Declaring a set of directories turns siblings into locally linked packages, so a dependency on one resolves to the folder rather than the registry. That single change is what makes a monorepo work without publishing anything between edits.
The whole repository resolves together and records one tree, which is what keeps versions aligned across packages and makes a single install sufficient. It also means a change in one package can move a shared dependency for all of them, which is visible in the lockfile diff.
A dependency on a sibling points at the working directory, so an edit is immediately visible with no build or publish step. The consequence is that what runs locally is the source rather than the packaged form, which is why publishing can still surprise you.
A specifier that refuses to resolve from the registry guarantees the local package is used and is rewritten to a real range when publishing. It removes the failure where a local dependency silently resolves to a stale published version that happens to exist.
Scripts can be targeted at one workspace or at all of them, which is what makes a repository-wide build or test one command. The flags differ between tools, and running across all packages exposes ordering questions that a single package never raises.
Two workspaces pinning different majors of one library recreate duplicate instances inside a repository that exists partly to prevent them. The single lockfile makes the skew visible, which is the advantage — it is a diff rather than a mystery.
N. npm, pnpm, Yarn and Corepack
A single global store hard-linked into each project means a version already downloaded costs almost nothing to reuse, in time and in disk. That architecture is the source of its speed advantage, and it is why its performance improves the more projects a machine has.
Only declared dependencies are linked into a package, so importing something undeclared fails immediately rather than working by accident. That turns phantom dependencies from a latent bug into a build error, which is the main reason to adopt it deliberately.
Removing the directory tree entirely and resolving through a manifest is faster and stricter, and it breaks any tool that expects to walk real directories on disk. Whether that trade works depends entirely on the toolchain, which is why adoption is a project-level decision.
Recording the expected tool and version in the manifest turns an implicit assumption into a declared one, which is what allows the environment to enforce it. Without it, the tool in use is whatever each contributor happens to have installed.
Proxying the command to the version the repository declares means contributors use the same tool without installing it deliberately. It closes the gap between declaring a package manager and actually getting one, which the manifest field alone leaves open.
Each tool spells the same intent differently: install exactly what is recorded and fail rather than update. Knowing all three is what lets a pipeline be written correctly for a repository that changes tools, and using the wrong one silently restores mutation.
O. Publishing
Listing what will ship before shipping it is the check that catches both failure directions — a missing build output, and a file that should never have left the machine. It takes seconds and it is the only review step before something becomes permanent.
A scoped package publishes restricted unless told otherwise, which is a safe default and a common first-publish failure. The command succeeds and the package is simply unavailable to anyone else, which reads as a registry problem rather than a flag.
The registry refuses to overwrite an existing version, which follows from immutability. The bump-and-tag command exists to do the version change and the tag in one step, keeping the published version and the repository history in agreement.
Publishing moves the default tag unless another is specified, so a prerelease published normally becomes what everyone installs. Publishing under a separate tag is what keeps a beta available to people who ask for it and invisible to everyone else.
Removal is restricted after a short window because other people's builds depend on the version existing, so the tool for a bad release is a warning attached to it. That warning reaches anyone installing it, which is more effective than removal would be anyway.
A signed attestation ties the published artifact to the build that produced it — the repository, the commit and the workflow — so a consumer can verify the tarball came from the source it claims. It moves trust from the publisher's account to the build system.
The configuration file carries registry locations, per-scope routing and authentication, which means a leaked token from it is publish access. It is also the mechanism for pointing internal scopes at an internal registry, which is the defense against one class of confusion attack.
P. The exports field and dual packaging
Once the field exists, anything it does not list is unimportable even though the file is present in the package. That is the intended behavior — it turns a package's public surface into a declaration rather than a side effect of its directory layout.
Different consumers can be given different files based on how they are importing, which is how one package serves several module systems and environments. The conditions are matched against the importing context rather than declared by the consumer.
Matching runs top to bottom and stops at the first hit, so the catch-all must be last and type declarations must come first or they are shadowed. It reads like a configuration object where order should not matter, and it is a list where it does.
Named subpaths and a pattern form let a package expose a family of entry points deliberately rather than by publishing a browsable folder. That is what allows internal reorganization without breaking consumers, since the public path is decoupled from the file location.
Prefixed specifiers give a package private aliases that resolve only inside itself, which is the internal counterpart to the public export map. They are useful for conditional internal resolution and for avoiding long relative paths without a build step.
Shipping both module formats allows a consumer graph to load both copies at once, producing one library with two independent states — the duplicate-instance problem arriving through format rather than through version. It is the reason dual publishing is harder than it looks.
Introducing the field to an existing package makes every previously working deep import fail, because the boundary did not exist before. It is a major change regardless of how compatible the listed entry points are, which is easy to miss because nothing in the package's own code changed.
Q. CommonJS, ESM and interop failure modes
The explicit extensions are unambiguous and override whatever the manifest declares, which makes them the escape hatch when a single file needs the other format. Mixing them deliberately is normal; mixing them accidentally produces a file interpreted differently from its neighbors.
The long-standing hard error no longer applies to a synchronous module graph, which invalidates a great deal of advice written before it changed. A graph containing top-level await still fails with a different error, and publishing in one format only remains a breaking change for consumers on older runtimes.
The asynchronous form is what lets older-format code load a module that is only published in the newer format, at the cost of the call site becoming asynchronous. That cost is the real constraint — it propagates outward through every caller that was synchronous.
A whole-module assignment in the older format surfaces as the default binding in the newer one, which is where an unexpected property access on an import comes from. The rule is consistent; it only looks arbitrary when the two formats are read as though they were the same thing.
Named bindings are detected statically by inspecting the module's source, so anything assigned dynamically or conditionally is invisible to the detection. A package whose exports are built in a loop therefore offers only a default binding, with no error explaining why.
Using it forces the entire module graph into the newer format and makes the module asynchronous to load, which can be exactly what prevents a package from being required at all. It is a small syntactic feature with a packaging-level consequence.
R. Audit and vulnerability handling
The resolved tree is submitted to an advisory service and matching advisories are reported, so the output is a function of both your tree and the database on that day. That is why the same tree can be clean one week and not the next with nothing installed in between.
A severity rating describes the vulnerability in the abstract, not whether your application can reach the affected code — a critical advisory in a build-only tool may be unreachable from anything you ship. Triage means asking about reachability, which the report cannot answer.
The forcing option accepts upgrades across major versions, which means it can introduce breaking changes while claiming to fix something. It is a code change that happens to be generated, so it deserves the same review as one written by hand.
A nested dependency cannot move until its parent permits a newer range, which is why a fix is often unavailable even though a patched version exists. The options are waiting for the parent, forcing it locally, or removing the dependency chain.
Restricting the audit to what actually ships answers the question the report is usually being asked to answer, and it typically removes most of the findings. The development-only findings are still worth knowing about, for a different reason — they run on developer machines.
When no fixed version exists the tooling has nothing to suggest, and the remaining options are forking, forcing a substitute, or removing the dependency. Recognizing that state quickly matters, because waiting for a fix that nobody is writing is the default outcome.
S. Supply-chain security
A name one character away from a popular one relies on a mistyped command or a copy-pasted instruction, and the payload runs at install time before anything is imported. The defense is reading the name in the command rather than the intent behind it.
A public package matching the name of an internal one can win resolution when the client is free to look at both registries, which turns an internal name into an attack surface. Scoping internal packages and pinning the scope to the internal registry is what closes it.
Disabling lifecycle scripts removes the most common install-time execution path, which is the highest-value single setting for an automated environment. It breaks packages that legitimately build on install, which is why it is paired with an allowlist rather than adopted blindly.
A committed lockfile with hashes detects a tarball that changed after it was first resolved, which is the mechanism protecting against a compromised mirror or cache. It only works if the file is committed and the strict install command is the one being run.
The common incident is not a new malicious package but a trusted one publishing a new patch containing an install script — which is why a range accepting patches is where the exposure is. Provenance and delayed adoption are the available defenses.
A generated inventory in a standard format is what makes the question of whether you ship an affected version answerable in minutes rather than days. It is worth generating before an incident, because the moment it is needed is the moment nobody has time to build it.
T. Bundling: what a bundler does and which tool does it
It walks the graph from an entry point and emits fewer, loadable files, which existed originally because browsers could not load modules at all. The reasons have changed — transformation, optimization, code splitting — while the mechanism has not.
Build tools resolve the same export map the runtime does and add conditions of their own for the environment being targeted. That is why a package can ship a browser-specific implementation, and why a condition missing from the map produces a surprising file choice.
The fast development path and the optimized build are different pipelines with different plugins and different assumptions, so they can genuinely disagree. A bug that appears only in the built output is not mysterious once that difference is taken seriously.
The tools occupy different roles — fast transformers, a library-oriented bundler, an application-oriented one, and an orchestrator combining them. Choosing by benchmark alone misses that they are solving different problems, which is why most projects end up using several.
Marking peers as external stops a library bundling its own copy, so the consuming application supplies exactly one. Omitting it is how a library ships a second copy of a framework and produces the duplicate-instance failure inside someone else's application.
U. Tree shaking, code splitting and source maps
Elimination depends on the imports and exports being analyzable without running the code, which the older format's dynamic requires defeat. That is why shipping only the older build silently disables the optimization for every consumer, with no warning and a larger bundle.
Declaring that modules can be dropped when nothing imports from them is what lets a bundler remove unused files entirely rather than merely unused bindings. Mislabeling it removes something that was doing work on import — a stylesheet, a polyfill, a registration — and the failure is a missing effect rather than an error.
A re-exporting index means importing one symbol can pull the whole package into the graph when purity cannot be established. It is a convenience at the call site paid for at build time, and it is the usual explanation for a bundle far larger than the code that was imported.
A dynamic import becomes its own chunk fetched when it is reached rather than up front, which moves cost from first load to first use. Choosing the split points is the design work — splitting something needed immediately adds a round trip and no benefit.
Separating rarely-changing dependencies means a deploy invalidates only the code that changed rather than everything a visitor has cached. The gain depends on the split being stable, so a chunking strategy that renames files on every build gives it all back.
One rewrites the code to be smaller and the other compresses bytes in transit, and they are not alternatives — compression of minified code is smaller than either alone. Skipping the first because the second is enabled leaves a real saving on the table.
They map generated output back to the original source so a stack trace is readable, and serving them publicly publishes that source. The decision is therefore about who should be able to read the code, which is why they are commonly generated and uploaded rather than deployed.
V. Transpilers, targets and Node versions
Syntax is rewritten at build time and costs nothing at run time; a missing runtime capability needs actual code shipped alongside. Conflating them is why a build targeting an old environment still fails there — the syntax was handled and the missing method was not.
A lower target emits more helper code to emulate newer syntax, so an over-conservative setting costs bytes and speed for environments nobody uses. Choosing it from the actual audience rather than from caution is one of the cheaper size wins available.
One shared query drives the transpiler, the prefixer and the bundler, so they cannot disagree about what is supported. Configuring them independently is how a build emits prefixed styles for a browser whose syntax it no longer transpiles for.
Three independent settings controlling the syntax emitted, the ambient declarations assumed available, and the module format produced. They are frequently changed together and they answer different questions, which is why changing one and expecting another's effect is a common source of confusion.
Nothing about the type system exists at run time, so a value arriving from outside the program is unchecked regardless of how it is annotated. The compiler also emits files rather than bundling them, which is why it is a type checker and a transpiler and not a build system.
It declares the supported runtime range, and whether that declaration is advice or a hard stop depends on a separate strictness setting. Declaring it without enabling enforcement documents an expectation that nothing checks, which is the usual configuration.
A version file pins the runtime itself for anyone using a version manager, which the manifest's declaration only documents. The two are complementary: one states what is supported, the other arranges for the right one to be in use.
Keentune is not affiliated with or endorsed by the organizations whose documentation informs these maps.
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.