Skip to content

Commit

Permalink
Configurable watch root (#9424)
Browse files Browse the repository at this point in the history
  • Loading branch information
jondlm authored Dec 19, 2023
1 parent 3e7d568 commit cf5e129
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/core/core/src/Parcel.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ export default class Parcel {
let resolvedOptions = nullthrows(this.#resolvedOptions);
let opts = getWatcherOptions(resolvedOptions);
let sub = await resolvedOptions.inputFS.watch(
resolvedOptions.projectRoot,
resolvedOptions.watchDir,
(err, events) => {
if (err) {
this.#watchEvents.emit({error: err});
Expand Down
2 changes: 1 addition & 1 deletion packages/core/core/src/RequestTracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1217,7 +1217,7 @@ async function loadRequestGraph(options): Async<RequestGraph> {
let snapshotKey = hashString(`${cacheKey}:snapshot`);
let snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
let events = await options.inputFS.getEventsSince(
options.projectRoot,
options.watchDir,
snapshotPath,
opts,
);
Expand Down
9 changes: 9 additions & 0 deletions packages/core/core/src/resolveOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ export default async function resolveOptions(
? path.resolve(outputCwd, initialOptions.cacheDir)
: path.resolve(projectRoot, DEFAULT_CACHE_DIRNAME);

// Make the root watch directory configurable. This is useful in some cases
// where symlinked dependencies outside the project root need to trigger HMR
// updates. Default to the project root if not provided.
let watchDir =
initialOptions.watchDir != null
? path.resolve(initialOptions.watchDir)
: projectRoot;

let cache =
initialOptions.cache ??
(outputFS instanceof NodeFS
Expand Down Expand Up @@ -180,6 +188,7 @@ export default async function resolveOptions(
shouldProfile: initialOptions.shouldProfile ?? false,
shouldTrace: initialOptions.shouldTrace ?? false,
cacheDir,
watchDir,
entries: entries.map(e => toProjectPath(projectRoot, e)),
targets: initialOptions.targets,
logLevel: initialOptions.logLevel ?? 'info',
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ export type ParcelOptions = {|

shouldDisableCache: boolean,
cacheDir: FilePath,
watchDir: FilePath,
mode: BuildMode,
hmrOptions: ?HMROptions,
shouldContentHash: boolean,
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cache.ensure();

export const DEFAULT_OPTIONS: ParcelOptions = {
cacheDir: path.join(__dirname, '.parcel-cache'),
watchDir: __dirname,
entries: [],
logLevel: 'info',
targets: undefined,
Expand Down
68 changes: 68 additions & 0 deletions packages/core/integration-tests/test/monorepos.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
assertBundles,
inputFS,
outputFS,
fsFixture,
ncp,
run,
overlayFS,
Expand Down Expand Up @@ -875,4 +876,71 @@ describe('monorepos', function () {
inputFS.chdir(oldcwd);
}
});

// This test ensures that workspace linked dependency changes are correctly
// detected in watch mode when `watchDir` is set to the monorepo root.
it('should correctly detect changes when watchDir is higher up in a project-only lockfile monorepo', async () => {
const dir = path.join(__dirname, 'project-specific-lockfiles');
overlayFS.mkdirp(dir);

await fsFixture(overlayFS, dir)`
packages
app
package.json:
{ "name": "app" }
pnpm-lock.yaml:
lockfileVersion: 5.4
index.js:
import {msg} from 'lib';
console.log(msg);
node_modules
lib -> ${path.join(
__dirname,
'project-specific-lockfiles',
'packages',
'lib',
)}
lib
package.json:
{ "name": "lib" }
pnpm-lock.yaml:
lockfileVersion: 5.4
index.js:
export const msg = "initial";`;

let b = await bundler(path.join(dir, 'packages', 'app', 'index.js'), {
inputFS: overlayFS,
watchDir: path.join(dir),
});

let builds = 0;

return new Promise((resolve, reject) => {
// 1. Increment the build counter and modify `packages/lib/index.js` which
// should trigger a subsquent build.
//
// 2. Ensure the changed asset was detected and built
b.watch(async (err, buildEvent) => {
builds++;

if (builds < 2) {
await overlayFS.writeFile(
path.join(dir, 'packages', 'lib', 'index.js'),
'export const msg = "changed-NcMB9nA7"',
);
} else {
const values = buildEvent?.changedAssets?.values();
if (values != null) {
const code = await Array.from(values)[0].getCode();
assert(code.includes('changed-NcMB9nA7'));
resolve();
} else {
reject(new Error('Changed assets missing.'));
}
}
}).then(sub => {
subscription = sub;
});
});
});
});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory exists for the `should correctly detect changes when watchDir is higher up in a project-only lockfile monorepo` test.

Without it I was getting a `Uncaught Error: No such file or directory` error stemming from [this line](https://github.com/parcel-bundler/parcel/blob/3b798e0456bbef951c684d43f96fda1fea386f62/packages/core/fs/src/OverlayFS.js#L375). The test itself doesn't care about watching the readable file system but it was necessary because `.watch` on `OverlayFS` subscribes to both the readable and writable filesystems.
3 changes: 3 additions & 0 deletions packages/core/parcel/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const commonOptions = {
'--config <path>':
'specify which config to use. can be a path or a package name',
'--cache-dir <path>': 'set the cache directory. defaults to ".parcel-cache"',
'--watch-dir <path>':
'set the root watch directory. defaults to nearest lockfile or source control dir.',
'--no-source-maps': 'disable sourcemaps',
'--target [name]': [
'only build given target(s)',
Expand Down Expand Up @@ -473,6 +475,7 @@ async function normalizeOptions(
return {
shouldDisableCache: command.cache === false,
cacheDir: command.cacheDir,
watchDir: command.watchDir,
config: command.config,
mode,
hmrOptions,
Expand Down
1 change: 1 addition & 0 deletions packages/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export type InitialParcelOptions = {|

+shouldDisableCache?: boolean,
+cacheDir?: FilePath,
+watchDir?: FilePath,
+mode?: BuildMode,
+hmrOptions?: ?HMROptions,
+shouldContentHash?: boolean,
Expand Down

0 comments on commit cf5e129

Please sign in to comment.