fix: namespace dev condition to holdmytask-dev; correct + test devcheck - #12
Conversation
The devcheck/src-dist machinery was inherited from @cldmv/slothlet but diverged in two ways that mattered for a normal module: 1. The /main export used the GENERIC `development` condition to route to src/, but the published package ships dist/ only (no src/). Any consumer running with `--conditions=development` (a common dev setting) therefore resolved @cldmv/holdmytask/main to ./src/hold-my-task.mjs, which isn't in the tarball -> ERR_MODULE_NOT_FOUND. This shipped in v1.6.2. Slothlet avoids it by namespacing its condition (`slothlet-dev`); do the same here with `holdmytask-dev` so a consumer's generic conditions can never route this package to a source tree it doesn't ship. 2. devcheck.mjs had its `!existsSync(dist)` guard commented out (so it would nag even after a build) and its installed-package guard checked only the immediate parent dir for "node_modules" — which never matches a SCOPED package (`node_modules/@cldmv/holdmytask`, parent is `@cldmv`). Restored the dist guard and fixed the check to detect a node_modules segment anywhere above the file (covers scoped + unscoped installs). Also: - Add `prepare: npm run build` so a fresh checkout's install produces dist/, keeping the package usable from a clone without setting the dev condition (build is a dependency-free ~0.25s file copy). - Route the condition through CI (ci.yml test_environment -> holdmytask-dev) and vitest (resolve/ssr conditions include holdmytask-dev) so tests still exercise src/. Verified: with dist absent, the package entry resolves to src only via holdmytask-dev; a generic `development` condition no longer does. - Add tests/DevCheck.test.vitest.mjs (7 cases) validating the guard across unbuilt-checkout, condition-set, dist-built, generic-condition, CI, scoped-install, and no-src scenarios.
Follow-up to the initial commit, correcting it against the established
normal-module devcheck fix in @cldmv/uuid (the repo that first solved the
generic-condition collision by namespacing to `uuid-dev`).
- devcheck.mjs: the warning is INTENTIONAL whenever src/ is present and the
dev condition isn't set - a built checkout has both src/ and dist/, and the
developer should be running from src/ via the condition, so flagging that
they're silently on dist/ is the point. Removed the `!existsSync(dist)`
guard added in the previous commit (which wrongly silenced it after a
build). Also removed the node_modules/installed-package guard: the published
package ships neither src/ nor devcheck.mjs, so index.mjs's
`import("./devcheck.mjs")` just fails and is ignored - it never runs for
consumers, so there's nothing to guard. Guard logic now mirrors uuid.
- Dropped the `prepare: npm run build` script - not part of the reference
pattern (uuid has none); the nag model expects you to build or set the
condition, not auto-build on install.
- vitest config: carry the condition into forked workers via
`test.nodeOptions` + `test.env.NODE_ENV` (mirrors uuid) rather than relying
on resolve/ssr conditions alone.
- Reverted the ci.yml `test_environment` change - uuid leaves it at the
reusable-workflow default and lets the vitest config carry the condition.
- Updated DevCheck tests to the corrected behavior (notably: STILL nags when
dist/ is present but the condition is unset).
…id's NODE_ENV logic The previous commit over-corrected by mirroring @cldmv/uuid's devcheck verbatim, which regressed the trigger to uuid's NODE_ENV-coupled form. That form is wrong for what devcheck detects, because ONLY the `--conditions=holdmytask-dev` condition selects src/ (NODE_ENV does not): - NODE_ENV=development with no condition -> uuid stays silent, but the package is actually resolving to dist/ (false negative - the exact case to catch). - condition set but NODE_ENV unset -> uuid nags even though you're correctly on src/ (false positive). Restore the condition-only trigger, and additionally detect the condition in process.execArgv, not just NODE_OPTIONS: node accepts `--conditions=` on the CLI (landing in execArgv) and that's how vitest passes it to workers - a probe showed NODE_OPTIONS is undefined in a worker while execArgv carries the flag, so the NODE_OPTIONS-only check (both mine originally and uuid's) would have spuriously fired inside the CommonAliases entry-import test and only avoided it by racing devcheck's fire-and-forget import. Checking both makes it correct and deterministic. Kept from the reference direction: the nag stays on after a build (no `!existsSync(dist)` guard). Kept my own additions uuid lacks: the scoped-aware node_modules install guard (protects git/tarball-install consumers) and the DevCheck test suite (now 9 cases, incl. execArgv form, NODE_ENV-doesn't-silence, still-nags-after-build, and scoped-install skip).
There was a problem hiding this comment.
Pull request overview
This PR updates the project’s “dev condition” mechanism to use a namespaced conditional export (holdmytask-dev) and aligns dev environment checks and Vitest configuration so local development and tests resolve the src/ entry intentionally (rather than accidentally via a generic development condition).
Changes:
- Renames the
./mainconditional export fromdevelopmenttoholdmytask-dev. - Updates
devcheck.mjsto detect the condition viaNODE_OPTIONSandprocess.execArgv, and to skip when installed undernode_modules. - Adds a dedicated Vitest test for devcheck behavior and updates Vitest config to include the new condition.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
package.json |
Switches the ./main conditional export to holdmytask-dev. |
devcheck.mjs |
Updates condition detection logic and adds an “installed package” guard. |
.configs/vitest.config.mjs |
Adds holdmytask-dev to resolver conditions and propagates it to Vitest workers. |
tests/DevCheck.test.vitest.mjs |
Adds coverage for devcheck behavior across env/execArgv/CI/installed scenarios. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…odule in ssr conditions Addresses PR #12 Copilot review: - devcheck.mjs: parse the actual `--conditions` values (from execArgv and NODE_OPTIONS, handling `=`/space/`-C`/comma forms) and match `holdmytask-dev` EXACTLY, instead of a substring `.includes()` that would false-positive on e.g. `--conditions=not-holdmytask-dev`. (Same fix as CLDMV/uuid#10.) Added regression tests: rejects a substring-containing condition; accepts holdmytask-dev among comma-separated conditions. - .configs/vitest.config.mjs: removed the `test.env.NODE_ENV=holdmytask-dev` override - it doesn't select the conditional export (that's `--conditions`, carried via nodeOptions) and forcing a non-standard NODE_ENV can confuse deps keying off test/development/production. Added `module` to ssr.resolve.conditions so a dependency's `module`-keyed export resolves the same under Vitest's SSR pipeline as in the non-SSR resolver.
The `./devcheck` -> `./devcheck.mjs` export pointed at a file not in the published `files` allowlist (verified via npm pack: devcheck.mjs isn't in the tarball), so `import "@cldmv/holdmytask/devcheck"` 404s for consumers. devcheck is an internal dev-time guard that index.mjs loads via a relative import, not the package export - nothing imports the subpath. Removing the dead export makes package.json honest. (Copilot review on #12.)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
devcheck.mjs:62
collect()splits condition strings on|as well as,. For Node's--conditions/NODE_OPTIONS the separator is comma;|is a valid character in a condition token (and is used in Vite's defaultdevelopment|productionmarker). Splitting on|can create false negatives, e.g.NODE_OPTIONS=--conditions=holdmytask-dev|productionwould silence devcheck even though Node would treat that as a single condition (so theholdmytask-devexport would NOT be selected and the developer would still be on dist/).
const conditions = [];
const collect = (value) => {
if (value) for (const c of value.split(/[,|]/)) if (c.trim()) conditions.push(c.trim());
};
tests/DevCheck.test.vitest.mjs:69
devcheck.mjssupports parsing--conditions <value>(space-separated) from argv/NODE_OPTIONS, but the test suite only covers the--conditions=<value>form. Adding a test for the space-separated form helps prevent regressions in the token-scanning logic.
test("stays silent when the condition is passed on the node CLI (execArgv)", () => {
// vitest passes --conditions to workers this way, so devcheck must detect it here too.
const { status, stderr } = runDevcheck({ src: true }, { nodeArgs: ["--conditions=holdmytask-dev"] });
expect(status).toBe(0);
expect(stderr).toBe("");
});
…e split) Addresses the second Copilot re-review on PR #12 (2 suppressed comments): - devcheck.mjs: stop splitting condition values on `,`/`|`. Node treats each `--conditions` occurrence as ONE literal condition and does not split on comma or pipe (verified: `--conditions=holdmytask-dev,x` and `--conditions=holdmytask-dev|production` do NOT enable holdmytask-dev). The old split caused a false negative - `holdmytask-dev|production` would silence devcheck while Node actually resolved to dist/. Now collect each value whole and match exactly. Fixed the test that wrongly asserted comma-joined silences (now asserts it nags), and added pipe-joined-nag plus space-separated (`--conditions holdmytask-dev`) and repeated-flag silent cases. - tests/DevCheck.test.vitest.mjs: added the standard project header block so it isn't an outlier vs the other test files.
|
Addressing the 2 suppressed comments from the latest review — both fixed in
Also added the standard project header block to the test file (it was missing it vs the other test files). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (1)
.configs/vitest.config.mjs:17
- Comment references
test.envcarrying the dev condition into workers, but this config no longer setstest.env. This is misleading for future maintenance; update the comment to only referencetest.nodeOptions(or reintroducetest.envif intended).
// conditions, so the usual ones are kept alongside it. `test.nodeOptions`/`test.env`
// below carry the same condition into forked test workers (for native imports of
// the package entry, e.g. CommonAliases importing index.mjs -> /main), so a bare
// local `npm test` resolves to src the same way CI does. Mirrors @cldmv/uuid.
Addresses the suppressed Copilot comment on PR #12 (.configs/vitest.config.mjs:17): the comment still mentioned `test.env` carrying the dev condition into workers, but that override was removed earlier in this PR. Comment now references only `test.nodeOptions`, which is what actually carries it.
|
Addressing the suppressed comment from the latest review ( Fixed in |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
devcheck.mjs:72
scan()collects the value after--conditions/-C, but it does not advance the loop index. This can incorrectly re-interpret the value token (e.g.--conditions --conditions=holdmytask-devwould be treated as enablingholdmytask-deveven though Node’s literal condition would be"--conditions=holdmytask-dev"). Incrementiafter consuming the next token and usecontinueto avoid double-processing.
const scan = (tokens) => {
for (let i = 0; i < tokens.length; i++) {
if (tokens[i] === "--conditions" || tokens[i] === "-C") {
if (tokens[i + 1] !== undefined) conditions.push(tokens[i + 1]);
} else if (tokens[i].startsWith("--conditions=")) {
conditions.push(tokens[i].slice("--conditions=".length));
} else if (tokens[i].startsWith("-C=")) {
conditions.push(tokens[i].slice("-C=".length));
}
}
};
devcheck.mjs:70
- The updated condition parsing explicitly supports Node’s short flag
-C/-C=..., but the new test suite doesn’t cover these cases. Adding coverage would help prevent regressions in the argument scanner across Node versions/runners that prefer-C.
if (tokens[i] === "--conditions" || tokens[i] === "-C") {
if (tokens[i + 1] !== undefined) conditions.push(tokens[i + 1]);
} else if (tokens[i].startsWith("--conditions=")) {
conditions.push(tokens[i].slice("--conditions=".length));
} else if (tokens[i].startsWith("-C=")) {
conditions.push(tokens[i].slice("-C=".length));
}
…nvalid -C= form Addresses the 2 suppressed comments on PR #12's latest review: - devcheck.mjs: the space-form branch (`--conditions x` / `-C x`) consumed tokens[i+1] as the value but did not advance the loop index, so a value that itself looks like a flag (e.g. `--conditions --conditions=x`) was double-processed. Now increment i past the consumed value token. - Dropped the `-C=` branch: Node rejects `-C=value` outright ("bad option"), so it can never appear in execArgv/NODE_OPTIONS - it was dead code. Valid forms are `--conditions=x`, `--conditions x`, and `-C x`. - Added -C short-flag test coverage (space form; verified Node rejects `-C=`).
|
Addressing the 2 suppressed comments from the latest review — both fixed in
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (1)
devcheck.mjs:87
- The message claims holdmytask "is loading from dist/" when the condition is not set. That isn’t necessarily true when
devcheck.mjsis run directly (as in the test fixtures), and can be misleading in an unbuilt checkout. Consider wording this as what will/would resolve by default instead of asserting the current runtime is definitely usingdist/.
console.error("📁 Source folder detected but the 'holdmytask-dev' condition is not set,");
console.error(" so holdmytask is loading from dist/ instead of src/.");
…om dist/" Addresses the suppressed Copilot comment on PR #12 (devcheck.mjs:87): the message asserted "holdmytask is loading from dist/", which isn't necessarily true - when devcheck runs standalone (test fixtures) or in an unbuilt checkout, dist/ may not exist at all. Reworded to describe default resolution behavior ("imports resolve to dist/ by default, or fail if it isn't built") rather than asserting the current runtime is definitely on dist/.
|
Addressing the suppressed comment from the latest review ( Fixed in |
🚀 What's Changed
💥 Breaking Changes
No breaking changes
✨ Features
No new features
🐛 Bug Fixes
📦 Dependencies
No dependency updates
🔧 Other Changes
👥 Contributors