Autodep for VSCode is an extension which automatically manages BUILD and BUILD.plz files in build-system-dependent Node.js projects.
Autodep is primarily configured to run upon the saving a supported file within VSCode. On save, it can:
- Work out exactly which build targets need to exist within a file's corresponding build rule, and write them to the appropriate location.
- Create and insert build rules based on a user-defined schema, if a pre-existing rule or
BUILDfile does not exist.
Users are able to control the type, definition and contents of the new rule to insert via an Autodep configuration file. Behaviour can be defined for the following three types of file:
module- a standard Node.js file, core to the functionality of the programfixture- a module which may not contain a test but is not core to the functionality of the program, often containing testing assets (mock data, commonised testing utilities, etc.)test- a file containing a test
Autodep is only useful in projects which use a build system. The extension is currently optimised for use with Please.
Autodep currently only handles Node.js files at the moment, and will only listen out for .ts, .js, .tsx, .jsx and .scss files, however it can still be used in multi-language, full-stack projects.
To enable the extension in your editor, you can add "autodep.runOnSave": true to your settings.json file.
Majority of configuration outside of this is handled via user-defined configuration files.
Autodep supports a high level of customisation, allowing a user to fine-tune its behaviour and achieve resilience against even the most esoteric cases one might find in a large, language-agnostic, build-system-dependent project.
A configuration file can be inserted anywhere within the project, so long as it exists within the specified root directory. It is recommended to place the main configuration file at the root of the directory. Subsequent files with tighter scope can be added further down the file tree, then linked via an extends field, similar to a tsconfig.json file. Extension configuration files will overwrite all clashing parent keys with their new values, except manage.rules, which will be concatenated into a (unique) superset of managed rule names.
To create a configuration file, just create an .autodep.yaml file. The configuration is strongly typed, and helpful errors will be shown within VSCode if configuration has been written incorrectly. Fields are not allowed to be empty, but no fields are mandatory, so feel free to remove them entirely if they are not useful.
-
rootDir- the name of the directory which should be considered the root of the project. The main configuration file should not be defined at a higher scope than this directory, and preferably should be defined at its root. -
outDir- the name of the directory which should be considered the root of theoutordistdirectory, defaults to<rootDir>/plz-out/genrootDir: app outDir: app/dist
-
manage- Users are able outline their own build rule schema definitions in this section. By default, autodep is only aware offilegroupandgenrulerules, and will only createfilegrouprules.-
rules- This field is used to specify all the rules which Autodep should bother to check when walking aBUILDfile at the relevant points in the update process. Only the name needs to be specified here, as the shape is initially assumed to be:rule_name( name = 'string', srcs = [], deps = [], visibility = [], )
Deviations from this format can be specified in
manage.schema -
schema- Autodep aims to preserve as much formatting and abstraction as possible, and so users are able to specify the names and shapes of custom build rules in themanage.schemasection. Amanage.schemaentry must be an object against the name of the build rule.manage.schemaentry objects are of typemap[string, array]. Eacharraycan contain a combination ofstringelements, as well as objects with the following shape:value: str as: str
manage.schemaentry objects are only allowed to contain the following fields (none are mandatory, but at least one must exist to justify the schema entry): -
name- the identifier of the particular instance of the build rule. This field is limited to thestringtype. All other types will throw an error.Defaults to
{value: "name", as: "string"}if not specified. -
srcs- the field which the extension should use to identify whether a particular build rule should be updated based on the Node.js file which triggered the process. This field is limited to thestring,arrayandglobtypes. All other types will throw an error.Defaults to
{value: "srcs", as: "array"}if not specified. -
deps- the field which the extension should update once it has calculated which dependencies it needs to update. This field is currently limited to thestringandarraytypes. All other types will throw an error.Defaults to
{value: "deps", as: "array"}if not specified. -
visibility- the field which the extension should use to calculate the visibility of target and dependent build rules, if necessary. This field is currently limited to thestringandarraytypes. All other types will throw an error.Defaults to
{value: "visibility", as: "array"}if not specified. -
testOnly- the field which the extension should use to calculate the visibility of any target and dependent build rules, if necessary. This field is currently limited to thebooltype. All other types will throw an error.Defaults to
{value: "test_only", as: "bool"}if not specified.
Multiple values can be specified against each of these fields.
When using the shorthand syntax (entering the field alias as a
string); the entry will be treated as thevaluevalue, and theasvalue will be derived from the respective field's default (specified above).manage: schema: my_custom_rule: srcs: - my_custom_srcs_alias js_library: name: - value: id as: string srcs: - srcs - value: src as: string - value: srcs as: glob deps: - value: dependencies as: array visibility: - value: visibility as: array genrule: srcs: - outs
-
-
knownTargets- This field allows you to specify a set of paths (relative to therootDir) and map them directly to targets. Sometimes, it is difficult to define a schema entry for a particular type of rule, and this field is an escape hatch for those situations. This may come in handy for managing the importing of generated files with names not explicitly set by the user, or defined within the implementation of a custom build rule, and therefore not discoverable by looking at aBUILDfile directly.This field should be used as a last resort, but it may be necessary.
manage: knownTargets: 'path/to/generatedFile.ts': //path/to/awkwardly_defined_build_target
-
match- This section allows you to specify three matcher fields, used to differentiate betweenmodule,testandfixturefiles. Each field can be defined either as aregexstring or an array ofextnameentries.moduledefaults to.*?\.(js|jsx|ts|tsx)$.fixtureis unmatchable by default.testdefaults to.*?\.(spec|test)\.(js|jsx|ts|tsx)$.
These fields are especially important for those who want fine-grained control over how Autodep handles the updating of particular types of files.
match: module: .*?\.(js|jsx|ts|tsx|scss|json)$ fixture: - .mockData.tsx - .fixture.json - .specialFile.module.js test: .*?\.(spec|test)\.(js|jsx|ts|tsx)$
-
log- Autodep provides 5 levels of logging output within VSCode; this field is used to determine which logging levels Autodep should bother populating. Each logging level is given its own channel in theOUTPUTsection of VSCode.trace- for verbose output regarding every decision Autodep is making when calculating dependencies. Useful for debugging and/or deepening your understanding of how targets are resolved by the extension.info- only the most important information about the update process: which stage of dependency resolution Autodep is currently taking care of, whether the update was successful or not, how long the process took from the point of saving and occasionally a copy of the stack trace for a particularly fatal error.debug- useful if you're interested in the configuration used during a particular run. Also used during the development of the extension.warn- used for non-fatal errors and standard warnings; useful when debugging if things are not working as expectederror- legitimate errors and/or fatal error reporting, complete with stack traces when available.
log: - trace - debug - info - warn - error
-
excludeNodeModules- this field allows you to specify whethernode_modulesdependencies should be included within the resolved set of dependencies. If you handle the compilation and inclusion of third-party dependencies with different tooling to your first-party dependencies, you can leave this off. Defaults tofalse.excludeNodeModules: true
-
enablePropagation- By default, Autodep will expect theBUILDfile for a particular Node.js file to be a direct sibling. This is the optimal location for aBUILDfile when using a build system, as you should only be building the dependencies to absolutely need. For existing projects who may have singleBUILDfiles responsible for several separate targets, or even several separate directories, you may instead want Autodep to update an existingBUILDfile which resides further up the file tree. This field allows you to opt in to that behaviour.Note that if this is turned on, Autodep will walk the entire filesystem until it finds a
BUILDfile, then update that file. It is recommended not to opt in to this behaviour if you are not willing to stay on top of the scoping and location ofBUILDtargets; this is especially true when paired with wildcardglobmatchers insrcsfields. Defaults tofalse.enablePropagation: false
-
onCreate- This section is where you can specify how Autodep should handle the absence of a pre-existingBUILDfile, or build rule. This situation occurs often, primarily when creating new files in the project.fileExtname- the extension of theBUILDfile. Defaults to an empty string, so a newly created file will be namedBUILD. Setting it toplz, for instance, would mean a newly created file would be namedBUILD.plz. Value is completely arbitrary.fileHeading- An optional comment which can be left at the top of a newly created file upon creation. If a value is set here and in theonUpdatesection, it will be overwritten on the next update. This is defined as a standard multiline string, and is converted into a comment by the extension. useful for"DO NOT EDIT"-style comments.
The following fields can be specified either at the top-level ofonCreate, or with more specificity under theonCreate.module,onCreate.fixtureandonCreate.testsub-fields:-
name- the name of the rule you wish to be created. Should correspond to a key inmanage.schema -
targetFormat- a formatter string representing the value which should be passed to thenamefield of the new build rule. The typing of the field will be the first item inmanage.schema[key].name, or the default. There are a few magic tokens available to help format the target, derived from the relative path of the target Node.js file:<basename>- the name of the file, complete with file extension<filename>- the name of the file, without file extension<firstname>- the first part of the filename, before the first".", if there are several in the basename, e.g.FirstName.spec.tsx<path>- the full path to the dependency, relative to theBUILDfile. For sibling files, this will be identical to the<basename>.
These will be interpolated appropriately at runtime. -
explicitSrcs- Whether the new rule should be created with thesrcsexplicitly listed, or whether it should be created using the specifiedonCreate.globMatchersconfiguration. Defaults totrue, as granularity is the optimal approach for build-system-dependent projects. When set to false, the typing of thesrcsfield will be the first item inmanage.schema[key].srcs, or the default. -
omitEmptyFields- Whether the new rule should omit the keyword argument if its value is empty. Forarraytypes, this would be an array of length0; forstringtypes, this would be a string of length0.- Currently unimplemented as of August 2022.
-
globMatchers- ifexplicitSrcsis set to true, a user is able to specify the values of theexcludeandincludefields in a glob declaration. These use the familiarglobmatcher syntax.globfields validate potential files by:- checking whether
includefield has a length greater than1, - checking at least one entry matches the candidate filename,
- checking whether the
excludefield either has a length of0, OR that every entry fails to match the candidate filename.
- checking whether
-
subinclude- For specifying imports needed for the newly created build rule to work within the project. If the import already exists in theBUILDfile within asubincludestatement, then it will be skipped, else the specified imports will be added to theBUILDfile, either by creating a newsubinclude, or merging the imports into any existingsubinclude.
The following fields can be specified either at the top-level ofonCreateor with more specificity, under theonCreate.moduleandonCreate.fixturesub-fields. They don't make much sense in the context of atestrule:initialVisibility- the value which should be passed to thevisibilityfield of the new build rule. The typing of the field will be the first item inmanage.schema[key].visibility, or the default.testOnly- the value which should be passed to thetestOnlyfield of the new build rule. The typing of the field will be the first item inmanage.schema[key].testOnly, or the default.
onCreate: fileExtname: plz fileHeading: |- My multiline onCreate file heading DO NOT EDIT module: name: my_custom_module_rule targetFormat: <filename>_custom_target explicitSrcs: true # will now ignore `globMatchers`field omitEmptyFields: true initialVisibility: - PUBLIC globMatchers: include: - '**/*.ts' - '**/*.tsx' exclude: - '**/*.spec.*' fixture: name: filegroup targetFormat: <firstname>_fixture testOnly: true test: name: my_test_rule targetFormat: <firstname>_test subinclude: - //path/to/build_defs:my_test_rule explicitSrcs: false omitEmptyFields: true globMatchers: include: - '**/*.spec.*' exclude: - '**/some/excludeable/file.spec.*'
-
onUpdate- This section is where you can specify how Autodep should handle the updating of a pre-existingBUILDfile, or build rule. This situation occurs often, primarily when saving a file with a pre-existing rule.fileHeading- An optional comment which can be left at the top of a newly created file upon creation. If a value is set here and in theonCreatesection, any pre-existingonCreatecomment will be overwritten on the next update. This is defined as a standard multiline string, and is converted into a comment by the extension. useful for"DO NOT EDIT"-style comments.
The following fields can be specified either at the top-level ofonUpdateor with more specificity, under theonUpdate.module,onUpdate.fixtureandonUpdate.testsub-fields:-
omitEmptyFields- Whether the new rule should omit the keyword argument if its value is empty. Forarraytypes, this would be an array of length0; forstringtypes, this would be a string of length0.- Currently unimplemented as of August 2022.
-
subinclude- For specifying imports needed for the newly created build rule to work within the project. If the import already exists in theBUILDfile within asubincludestatement, then it will be skipped, else the specified imports will be added to theBUILDfile, either by creating a newsubinclude, or merging the imports into any existingsubinclude.For
onUpdate, this field is used to persist important build rule imports, if you tend to allow developers to manually tamper withBUILDfiles.
onUpdate: fileHeading: |- My multiline onUpdate file heading DO NOT EDIT module: omitEmptyFields: true fixture: omitEmptyFields: false test: omitEmptyFields: true subinclude: - //stop/removing/my/subincludes:subincludes
-
ignore- This section is useful for selectively turning off Autodep functionality for particular files and directories, as well as ignoring particular targets in dependency resolution, for whatever reason.pathentries can be specified asglobmatchers for brevity.paths- Particular paths Autodep should not attempt to run for. This can be specified at the top-level ofignore, or with more specificity under theignore.module,ignore.testandignore.fixturesub-fields. Any values added at the top-level fields are disregarded for the respective file type if its sub-field is explicitly populated, else the file type inherits the top-level ignore values.targets- Particular targets Autodep should not include when calculating which dependencies to write to the target build rule'sdepsfield. This can be specified at the top-level ofignore, or with more specificity under theignore.module,ignore.testandignore.fixturesub-fields.
ignore: paths: - '**/webpack.{production,test,development}.*' - path/to/myIgnoredDirectory/* module: paths: - '**/webpack.{production,test,development}.*' - path/to/myOnlyIgnoreModuleDirectory/* targets: - //dont/include/this/awkward/dep fixture: targets: - //dont/include/this/awkward/dep - //dont/include/this/awkward/dep:only_for_fixtures test: targets: - //dont/include/this/awkward/dep - //dont/include/this/awkward/dep:only_for_tests
-
Autodep does not currently support anything but explicit filenames and subpaths in
srcsarray fields. There are plans to be able to support build targets insrcsfields in the future. -
Autodep plans to support the cleanup of old
BUILDtargets in the future; for now, deleting a file, will leave an orphaned build rule in the correspondingBUILDfile -
Autodep does not currently support lambdas and list comprehensions in
BUILDfiles. The internal parser will be completed in future, but only the most common syntax is covered at the moment. The same is true for complex type-hints. -
Autodep will not "correct" visibility issues with pre-existing build targets. For instance, if a dependency
BUILDfile is structured like this:my_rule( name = "my_package", srcs = ["index.ts"], deps = [":a", ":b"], visibility = ["PUBLIC"] ) my_rule( name = "a", srcs = ["a.ts"], visibility = ["only/this/directory/..."] ) my_rule( name = "b", srcs = ["b.ts"], visibility = ["only/this/directory/..."] )
And the dependency is imported like this:
import {thing} from 'path/to/myPackage/b';
It will still try to include
//path/to/my_package:b- as it should.To correct the build, either:
- Correct the visibility of the dependency
- Import from
path/to/myPackageinstead, aspath/to/myPackage/index.tsis the visible file, notpath/to/myPackage/b.ts.