-
Notifications
You must be signed in to change notification settings - Fork 345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Added --ignore-files option and build now ignores artifacts folder #753
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for digging into this issue!
It's on the right track but here are some change requests:
--ignore-files
should be a global option so that other commands can benefit from it- The
ignoreFiles
andartifactsDir
logic needs to move out ofbuild
and intoFileFilter
directly since both lint and run's file watcher use theFileFilter
too. Both of those commands will need the sameignoreFiles
andartifactsDir
logic.
Seems that there are only 2 tests using |
I think so. It is only used by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is on the right track, thanks! Let me know if you get stuck.
src/cmd/run.js
Outdated
@@ -257,14 +262,21 @@ export default async function run( | |||
} | |||
|
|||
log.info('The extension will reload if any source file changes'); | |||
const createWatcher = (...args) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The coverage report says this function never gets executed by the test suite. I think you'd have two options:
- Try to think of a test that calls
run()
that covers it - Replace
createWatcher
with a dependency injection parameter and write a separate unit test for it. This is the typical pattern we use elsewhere because it usually means the tests are easier to write.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to solve #751 to do the "run" test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, yes of course! OK, let's try to land #751 first so you can merge it into this branch when it hits master.
src/cmd/run.js
Outdated
@@ -257,14 +262,21 @@ export default async function run( | |||
} | |||
|
|||
log.info('The extension will reload if any source file changes'); | |||
const createWatcher = (...args) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not see args
get mutated because who knows what babel does to it under the hood. Could it be done more directly, like:
const createWatcher = (watcherParams, ...args) => {
// ...
return defaultWatcherCreator({
...watcherParams,
shouldWatchFile: (file) => fileFilter.wantFile(file),
}, ...args);
}
src/cmd/run.js
Outdated
}); | ||
args[0].shouldWatchFile = (file) => fileFilter.wantFile(file); | ||
return defaultWatcherCreator(...args); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this callback necessary? I see that sourceDir
and artifactsDir
are already passed to reloadStrategy
and eventually the watcher where it instantiates its own FileFilter
anyway (in lieu of a callback). Maybe you can just pass ignoreFiles
all the way through? Sorry, this code has been rewritten a couple times to try and alleviate the headache (but it still hurts).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I should make reloadStrategy
accept ignoreFiles
and build shouldWatchFile
inside reloadStrategy
.
Also maybe we should remove FileFilter
from the watcher so it doesn't depend on FileFilter
anymore. Make shouldWatchFile
default to () => true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there is probably a bit too much complexity here.
src/program.js
Outdated
@@ -246,6 +246,12 @@ Example: $0 --help run. | |||
describe: 'Show verbose output', | |||
type: 'boolean', | |||
}, | |||
'ignore-files': { | |||
alias: 'i', | |||
describe: 'Files to ignore.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be more explicit and should show an example of ignoring multiple files. It should say that the file must be an absolute path. I think an example of multiple paths would look like this, no? --ignore-files /path/to/first.js /path/to/second.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be relative path. FileFilter
resolves it with sourceDir
.
tests/unit/test-cmd/test.build.js
Outdated
@@ -48,6 +53,50 @@ describe('build', () => { | |||
); | |||
}); | |||
|
|||
it('ignore artifacts dir if included by source dir when zipping', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rejoice! Since you moved all of the ignoring logic, you no longer need such a heavyweight test. Here's what you can do:
- you can delete all of the fixture directories (with the manifest, etc)
- you can simply assert that build is configuring the file watcher correctly with its value of
ignoreFiles
. Here are some examples of how we do things like that.
@@ -0,0 +1,87 @@ | |||
/* @flow */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call on moving this to its own file. You also need to move the tests out of test.build.js. The new ignoreFiles
needs test coverage too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found some bugs when writing tests for FileFilter.
it('ignore artifactsDir and its content', () => {
const filter = new FileFilter({
artifactsDir: 'artifacts',
});
assert.equal(filter.wantFile('artifacts'), false);
assert.equal(filter.wantFile('artifacts/some.js'), false); // assertion error
});
It might be fine for build.js
, since the zipDir
will skip the content if the parent dir is skipped.
But it cause problem for watcher.js
. Make the content inside artifactsDir
becomes wanted files. This bug applies to .git
and node_modules
folders as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how should we fix it. Just explicitly ignore all content inside the folder?
filesToIgnore = [
'**/*.xpi',
'**/*.zip',
'**/.*', // any hidden file
+ '**/.*/**/*', // any content in the hidden folder
'**/node_modules',
+ '**/node_modules/**/*',
],
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These issues are both resolved in the patch now.
How do I fix this error? BTW, what does the vertical bar mean in type definition? I can't find them in flow's document.
|
Huh, looks like a Flow bug. Flow is smart enough to see that you checked the type of the variable in an if statement but it seems to forget the if statement after one line of code. I moved the diff --git a/src/util/file-filter.js b/src/util/file-filter.js
index 23a941d..13d046a 100644
--- a/src/util/file-filter.js
+++ b/src/util/file-filter.js
@@ -68,11 +68,13 @@ export class FileFilter {
* Resolve relative path to absolute path if sourceDir is setted.
*/
resolve(file: string): string {
- if (typeof this.sourceDir === 'string') {
+ if (this.sourceDir !== undefined) {
+ const resolvedPath = path.resolve(this.sourceDir, file);
log.debug(
- `Adding sourceDir ${this.sourceDir} to the beginning of file ${file}`
+ `Resolved path ${file} with sourceDir ${this.sourceDir || ''} ` +
+ `to ${resolvedPath}`
);
- return path.resolve(this.sourceDir, file);
+ return resolvedPath;
}
return normalizeResolve(file);
}
I also made the debug statement more verbose to help detect the situation you mentioned above when the path is absolute (which is when it won't be pre-pended with |
Sigh, I can't find it in the docs either. It's a very important feature. If you have a function like this: type Params = {|
size: 'small' | 'large',
|}
function render({size}: Params) {
} And you call it like this: render({size: 'small', color: 'blue'}); then Flow will raise an error because |
src/util/file-filter.js
Outdated
*/ | ||
export class FileFilter { | ||
filesToIgnore: Array<string>; | ||
sourceDir: string | typeof undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this works. I suggested the wrong thing originally but I was thinking of:
sourceDir: ?string;
They are pretty much the same though. The one with the question marks allows it to be null
which is the only difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for all the quick turnarounds! This looks good to me but because it touches so many commands, I'd like @rpl to also review it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @eight04
My apologies for the waiting time, this PR looks almost great, thanks for working on it!
I've added some comments related to some minor changes (most of them are small nits, the most important ones are related to the yargs configuration of the new cli option).
Let me know if any of the comment is not clear enough or if you disagree with any of the requested changes.
tests/unit/test-cmd/test.build.js
Outdated
@@ -49,6 +49,27 @@ describe('build', () => { | |||
); | |||
}); | |||
|
|||
it('configures a build', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test case description could probably be a bit more specific (e.g. it('configures a build command with the expected fileFilter', ...)
)
tests/unit/test-cmd/test.run.js
Outdated
@@ -299,6 +300,23 @@ describe('run', () => { | |||
assert.typeOf(callArgs.onChange, 'function'); | |||
}); | |||
|
|||
it('configure fileFilter, shouldWatchFile', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about it('configures a run command with the expected fileFilter', ...))
?
tests/unit/test-cmd/test.lint.js
Outdated
assert.equal(fileFilter.wantFile.called, true); | ||
assert.equal(fileFilter.wantFile.firstCall.args[0], 'manifest.json'); | ||
}); | ||
it('configures and passes a fileFilter to the linter', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about it('configures a lint command with the expected fileFilter', ...)
?
paths.forEach((file) => { | ||
assert.equal( | ||
path.resolve(src, file), | ||
path.resolve(src) + path.sep + normalizeResolve(file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use path.join(path.resolve(src), normalizeResolve(file))
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's OK? This test is to make sure:
normalizeResolve(relativePath) == stripRoot(path.resolve(root, relativePath), root)
@@ -246,6 +246,14 @@ Example: $0 --help run. | |||
describe: 'Show verbose output', | |||
type: 'boolean', | |||
}, | |||
'ignore-files': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this options should require an argument (by adding the requiresArg: true
to the yargs properties for the option).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that setting requiresArg
would force yargs to validate ignoreFiles
is not a empty list. Since the ignoreFiles
defaults to an empty list, this make yargs always argue Missing argument value: ignore-files
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is supposed to be part of the last review comments but it has not been submitted by github, and so I added it here as a separate comment.
@eight04 yargs can be very annoying sometimes :-)
Anyway, it looks like that we can use a combination of yargs option config properties to make it work as expected:
'ignore-files': {
alias: 'i',
describe: '...',
demand: false,
requiresArg: true,
type: 'array',
},
Basically it seems that we have to:
- omit the empty array currently set as its default value
- add a
demand: false
config to allow the --ignore-files option to be undefined (which is going to mean "that the option has not been specified") - add the
requiresArg: true
which force "--ignore-files" to have a value when specified
I briefly tried the above config locally and it seems to work as expected,
let me know if it doesn't for you.
src/program.js
Outdated
alias: 'i', | ||
describe: 'A list of glob patterns to define which files should be ' + | ||
'ignored. (Example: --ignore-files=path/to/first.js ' + | ||
'path/to/second.js **/*.log)', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the help message we should suggest to quote the patterns (e.g. the **/*.log
), or what is going to happen in most of the shells is that "the glob pattern will be expanded up front and a long list of the resulting files will be passed to the web-ext command" (instead of the single glob entry as expected).
e.g. --ignore-files=path/to/file.js "**/*.log"
src/util/file-filter.js
Outdated
sourceDir: ?string; | ||
|
||
constructor({ | ||
filesToIgnore = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FileFilter
now takes two parameters with two very similar names but they have different meaning, it would be nice to make it more clear by make the names a bit more descriptive (e.g. filesToIgnore
renamed to defaultFilesToIgnore
, and ignoreFiles
renamed to customFilesToIgnore
?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change ignoreFiles
to customFilesToIgnore
, every calls to createFileFilter
would also need to be changed to createFileFilter({sourceDir, artifactsDir, customFilesToIgnore: ignoreFiles})
.
How about to change filesToIgnore
to defaultIgnorePatterns
and keep ignoreFiles
so createFileFilter
could be called nicely as createFileFilter({sourceDir, artifactsDir, ignoreFiles})
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eight04 yeah, I agree that not being able to use the ES6 syntax shortcut is a pity.
What I'd like to achieve is to make it clear that the FileFilter constructor property currently named filesToIgnore
is not overridden by the new ignoreFiles
property, but extended.
How about rename filesToIgnore
to baseIgnoredPatterns
(or commonIgnoredPatterns
) and leave ignoreFiles
unmodified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @eight04
Thanks for the last round of updates, This is almost ready!
Follows some additional comments on the last two pending issues (the FileFilter contructor property names and the requiresArg option on the yargs option configuration).
src/util/file-filter.js
Outdated
sourceDir: ?string; | ||
|
||
constructor({ | ||
filesToIgnore = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eight04 yeah, I agree that not being able to use the ES6 syntax shortcut is a pity.
What I'd like to achieve is to make it clear that the FileFilter constructor property currently named filesToIgnore
is not overridden by the new ignoreFiles
property, but extended.
How about rename filesToIgnore
to baseIgnoredPatterns
(or commonIgnoredPatterns
) and leave ignoreFiles
unmodified?
1 similar comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR looks great, thanks @eight04!
Thanks again for all your work on this and especially for all the quick turnarounds. |
Nice work! 👍 |
Fixes #131, Fixes #186