-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2141 from embroider-build/side-watch-packages
Add better broccoli-side-watch package
- Loading branch information
Showing
13 changed files
with
3,559 additions
and
3,235 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/node_modules | ||
/src/**/*.js | ||
/src/**/*.d.ts | ||
/src/**/*.map | ||
/tests/**/*.js | ||
/tests/**/*.d.ts | ||
/tests/**/*.map |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# @embroider/broccoli-side-watch | ||
|
||
A micro library that allows watching folders for changes outside the `app` folder in Ember apps | ||
|
||
## Usage | ||
|
||
Let's assume you have a v2 addon with a package name of `grand-prix` somewhere in your monorepo that also contains your Ember app. | ||
|
||
Every time you change something in the source of that addon, you can rebuild it by watching the addon's build (currently using rollup). However, by default the host Ember app doesn't rebuild automatically, so you have to restart the Ember app every time this happens which is a slog. | ||
|
||
With this library, you can add the following to your `ember-cli-build.js` to vastly improve your life as a developer: | ||
|
||
```js | ||
const sideWatch = require('@embroider/broccoli-side-watch'); | ||
|
||
const app = new EmberApp(defaults, { | ||
trees: { | ||
app: sideWatch('app', { watching: [ | ||
'grand-prix', // this will resolve the package by name and watch all its importable code | ||
'../grand-prix/dist', // or you point to a specific directory to be watched | ||
] }), | ||
}, | ||
}); | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
testEnvironment: 'node', | ||
testMatch: [ | ||
'<rootDir>/tests/**/*.test.js', | ||
], | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "@embroider/broccoli-side-watch", | ||
"version": "1.0.0", | ||
"description": "Watch changes in other folders to rebuild Ember app", | ||
"keywords": [ | ||
"ember" | ||
], | ||
"main": "src/index.js", | ||
"files": [ | ||
"src/**/*.js", | ||
"src/**/*.d.ts", | ||
"src/**/*.js.map" | ||
], | ||
"scripts": { | ||
"test": "jest" | ||
}, | ||
"author": "Balint Erdi", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@embroider/shared-internals": "workspace:^", | ||
"broccoli-merge-trees": "^4.2.0", | ||
"broccoli-plugin": "^4.0.7", | ||
"broccoli-source": "^3.0.1", | ||
"resolve-package-path": "^4.0.1" | ||
}, | ||
"devDependencies": { | ||
"broccoli-node-api": "^1.7.0", | ||
"broccoli-test-helper": "^2.0.0", | ||
"scenario-tester": "^4.0.0", | ||
"typescript": "^5.1.6" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { dirname, join, resolve } from 'path'; | ||
import mergeTrees from 'broccoli-merge-trees'; | ||
import { WatchedDir } from 'broccoli-source'; | ||
import { getWatchedDirectories, packageName } from '@embroider/shared-internals'; | ||
import resolvePackagePath from 'resolve-package-path'; | ||
import Plugin from 'broccoli-plugin'; | ||
|
||
import type { InputNode } from 'broccoli-node-api'; | ||
|
||
class BroccoliNoOp extends Plugin { | ||
constructor(path: string) { | ||
super([new WatchedDir(path)]); | ||
} | ||
build() {} | ||
} | ||
|
||
interface SideWatchOptions { | ||
watching?: string[]; | ||
cwd?: string; | ||
} | ||
|
||
/* | ||
Doesn't change your actualTree, but causes a rebuild when any of opts.watching | ||
trees change. | ||
This is helpful when your build pipeline doesn't naturally watch some | ||
dependencies that you're actively developing. For example, right now | ||
@embroider/webpack doesn't rebuild itself when non-ember libraries change. | ||
*/ | ||
export default function sideWatch(actualTree: InputNode, opts: SideWatchOptions = {}) { | ||
const cwd = opts.cwd ?? process.cwd(); | ||
|
||
if (!opts.watching || !Array.isArray(opts.watching)) { | ||
console.warn( | ||
'broccoli-side-watch expects a `watching` array. Returning the original tree without watching any additional trees.' | ||
); | ||
return actualTree; | ||
} | ||
|
||
return mergeTrees([ | ||
actualTree, | ||
...opts.watching | ||
.flatMap(w => { | ||
const pkgName = packageName(w); | ||
|
||
if (pkgName) { | ||
// if this refers to a package name, we watch all importable directories | ||
|
||
const pkgJsonPath = resolvePackagePath(pkgName, cwd); | ||
if (!pkgJsonPath) { | ||
throw new Error( | ||
`You specified "${pkgName}" as a package for broccoli-side-watch, but this package is not resolvable from ${cwd} ` | ||
); | ||
} | ||
|
||
const pkgPath = dirname(pkgJsonPath); | ||
|
||
return getWatchedDirectories(pkgPath).map(relativeDir => join(pkgPath, relativeDir)); | ||
} else { | ||
return [w]; | ||
} | ||
}) | ||
.map(path => { | ||
return new BroccoliNoOp(resolve(cwd, path)); | ||
}), | ||
]); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
'use strict'; | ||
|
||
import { UnwatchedDir } from 'broccoli-source'; | ||
import sideWatch from '../src'; | ||
import { Project } from 'scenario-tester'; | ||
import { join } from 'path'; | ||
import { createBuilder } from 'broccoli-test-helper'; | ||
|
||
async function generateProject() { | ||
const project = new Project('my-app', { | ||
files: { | ||
src: { | ||
'index.js': 'export default 123', | ||
}, | ||
other: { | ||
'index.js': 'export default 456;', | ||
}, | ||
}, | ||
}); | ||
|
||
await project.write(); | ||
|
||
return project; | ||
} | ||
|
||
describe('broccoli-side-watch', function () { | ||
test('it returns existing tree without options', async function () { | ||
const project = await generateProject(); | ||
const existingTree = new UnwatchedDir(join(project.baseDir, 'src')); | ||
|
||
const node = sideWatch(existingTree); | ||
|
||
expect(node).toEqual(existingTree); | ||
}); | ||
|
||
test('it watches additional relative paths', async function () { | ||
const project = await generateProject(); | ||
const existingTree = new UnwatchedDir(join(project.baseDir, 'src')); | ||
|
||
const node = sideWatch(existingTree, { watching: ['./other'], cwd: project.baseDir }); | ||
const output = createBuilder(node); | ||
await output.build(); | ||
|
||
expect(output.read()).toEqual({ 'index.js': 'export default 123' }); | ||
|
||
const watchedNode = node | ||
.__broccoliGetInfo__() | ||
.inputNodes[1].__broccoliGetInfo__() | ||
.inputNodes[0].__broccoliGetInfo__(); | ||
expect(watchedNode).toHaveProperty('watched', true); | ||
expect(watchedNode).toHaveProperty('sourceDirectory', join(project.baseDir, 'other')); | ||
}); | ||
|
||
test('it watches additional absolute paths', async function () { | ||
const project = await generateProject(); | ||
const existingTree = new UnwatchedDir(join(project.baseDir, 'src')); | ||
|
||
const node = sideWatch(existingTree, { watching: [join(project.baseDir, './other')] }); | ||
const output = createBuilder(node); | ||
await output.build(); | ||
|
||
expect(output.read()).toEqual({ 'index.js': 'export default 123' }); | ||
|
||
const watchedNode = node | ||
.__broccoliGetInfo__() | ||
.inputNodes[1].__broccoliGetInfo__() | ||
.inputNodes[0].__broccoliGetInfo__(); | ||
expect(watchedNode).toHaveProperty('watched', true); | ||
expect(watchedNode).toHaveProperty('sourceDirectory', join(project.baseDir, 'other')); | ||
}); | ||
|
||
test('it watches additional package', async function () { | ||
const project = await generateProject(); | ||
project.addDependency( | ||
new Project('some-dep', '0.0.0', { | ||
files: { | ||
'index.js': `export default 'some';`, | ||
}, | ||
}) | ||
); | ||
await project.write(); | ||
|
||
const existingTree = new UnwatchedDir(join(project.baseDir, 'src')); | ||
|
||
const node = sideWatch(existingTree, { watching: ['some-dep'], cwd: project.baseDir }); | ||
const output = createBuilder(node); | ||
await output.build(); | ||
|
||
expect(output.read()).toEqual({ 'index.js': 'export default 123' }); | ||
|
||
const watchedNode = node | ||
.__broccoliGetInfo__() | ||
.inputNodes[1].__broccoliGetInfo__() | ||
.inputNodes[0].__broccoliGetInfo__(); | ||
expect(watchedNode).toHaveProperty('watched', true); | ||
expect(watchedNode).toHaveProperty('sourceDirectory', join(project.baseDir, 'node_modules/some-dep')); | ||
}); | ||
|
||
test('it watches additional package with exports', async function () { | ||
const project = await generateProject(); | ||
project.addDependency( | ||
new Project('some-dep', '0.0.0', { | ||
files: { | ||
'package.json': JSON.stringify({ | ||
exports: { | ||
'./*': { | ||
types: './declarations/*.d.ts', | ||
default: './dist/*.js', | ||
}, | ||
}, | ||
}), | ||
src: { | ||
'index.ts': `export default 'some';`, | ||
}, | ||
dist: { | ||
'index.js': `export default 'some';`, | ||
}, | ||
declarations: { | ||
'index.d.ts': `export default 'some';`, | ||
}, | ||
}, | ||
}) | ||
); | ||
await project.write(); | ||
|
||
const existingTree = new UnwatchedDir(join(project.baseDir, 'src')); | ||
|
||
const node = sideWatch(existingTree, { watching: ['some-dep'], cwd: project.baseDir }); | ||
const output = createBuilder(node); | ||
await output.build(); | ||
|
||
expect(output.read()).toEqual({ 'index.js': 'export default 123' }); | ||
|
||
const watchedNode = node | ||
.__broccoliGetInfo__() | ||
.inputNodes[1].__broccoliGetInfo__() | ||
.inputNodes[0].__broccoliGetInfo__(); | ||
expect(watchedNode).toHaveProperty('watched', true); | ||
expect(watchedNode).toHaveProperty('sourceDirectory', join(project.baseDir, 'node_modules/some-dep/dist')); | ||
}); | ||
}); |
This file contains 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
This file contains 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
Oops, something went wrong.