Modernize Djblets JavaScript setup.

Review Request #14646 — Created Oct. 23, 2025 and updated

Information

Djblets
release-5.x

Reviewers

This change makes a bunch of changes to modernize our JavaScript build
process across our products:

Support npm workspaces

In order to better support development across our many repos and
packages, this dramatically improves the way we handle npm workspaces.

We used to have a djblets/package.json file which defines our JavaScript
dependencies, and then a symbolic link from the repo root to make that
also available for npm install. This change removes the symbolic link,
and adds a new top-level package.json which defines workspaces as
djblets and .npm-workspaces/*. This allows creating symbolic links
in the workspaces directory in order to bring in development copies of
spina, ink, beanbag-eslint-plugin, or whatever other JS dependencies one
desires.

Depending on the package, this may require having a shared parent
directory and a symbolic link from ../node_modules to
./node_modules, since some things like to resolve to absolute paths
and then crawl up the directory tree (looking at you, tsc).

Update and consolidate JS dependencies.

The actual dependencies listed in djblets/package.json have been
simplified, using the new @beanbag/js-buildkit metapackage to ensure
build-time dependencies. We also no longer have an explicit dependency
on spina and backbone versions, preferring instead to just inherit
whichever versions we're getting from ink.

Use modern invocation for static media builder utilities.

We were hard-coding the locations for babel, lessc, rollup, and
uglifyjs based on node_modules/.bin. Because of the vagaries of npm
we can't necessarily assume that there's a node_modules in the current
directory, or that things will be in .bin. I've changed these to use
npm exec ... instead, allowing npm to locate the binary and invoke it.

Use terser instead of uglifyjs (opt-in).

UglifyJS lacks support for a lot of ES6+ features, and its cousin
UglifyES is unmaintained. I've changed our pipeline configuration to use
Terser instead, which is a fork of these which is actively maintained
and supports newer JS. The actual dependency for this will live in
@beanbag/frontend-buildkit.

Some of our upstream dependencies already depend on terser, so this
reduces our dependency footprint as well.

I was hoping we would be able to get sourcemaps for the fully-compressed
JS with this, but unfortunately the way that django-pipeline does
compressors won't allow for that.

Use modern pipeline setup for static media during tests.

Unit tests had their own pipeline configuration for some reason, which
didn't even include any of the rollup files. This has been replaced with
a call to the same build_pipeline_settings that we use everywhere
else.

Pulled in backbone types.

I'm working on upstreaming our changes and improvements to the type
definitions for Backbone, but in the meantime we can't just reference
the file from node_modules the way we did before. This has been copied
into the Djblets tree for the time being.

Updated to ESLint 9.

With the newer @beanbag/eslint-plugin bundled in
@beanbag/js-buildkit, we now use ESLint 9.

Simplify browserslist.

I've changed the browserslist config to exist inside the top-level
package.json and just specify baseline widely available. This mirrors
what we have in Review Board (though RB also has that in our shareable
babel config).

  • Set up Djblets into an environment that used npm workspaces to include
    Djblets, Review Board, @beanbag/frontend-buildkit, @beanbag/js-buildkit,
    @beanbag/jasmine-suites, and @beanbag/eslint-plugin.
  • Ran unit tests.
  • Ran eslint.
  • Built static media.
  • Built the rbintegrations extension to verify that static media
    building still worked for that.
Summary ID
Modernize Djblets JavaScript setup.
This change makes a bunch of changes to modernize our JavaScript build process across our products: # Support npm workspaces In order to better support development across our many repos and packages, this dramatically improves the way we handle npm workspaces. We used to have a `djblets/package.json` file which defines our JavaScript dependencies, and then a symbolic link from the repo root to make that also available for `npm install`. This change removes the symbolic link, and adds a new top-level `package.json` which defines workspaces as `djblets` and `.npm-workspaces/*`. This allows creating symbolic links in the workspaces directory in order to bring in development copies of spina, ink, beanbag-eslint-plugin, or whatever other JS dependencies one desires. Depending on the package, this *may* require having a shared parent directory and a symbolic link from `../node_modules` to `./node_modules`, since some things like to resolve to absolute paths and then crawl up the directory tree (looking at you, `tsc`). # Update and consolidate JS dependencies. The actual dependencies listed in `djblets/package.json` have been simplified, using the new `@beanbag/js-buildkit` metapackage to ensure build-time dependencies. We also no longer have an explicit dependency on spina and backbone versions, preferring instead to just inherit whichever versions we're getting from ink. # Use modern invocation for static media builder utilities. We were hard-coding the locations for `babel`, `lessc`, `rollup`, and `uglifyjs` based on `node_modules/.bin`. Because of the vagaries of npm we can't necessarily assume that there's a `node_modules` in the current directory, or that things will be in `.bin`. I've changed these to use `npm exec ...` instead, allowing npm to locate the binary and invoke it. # Use terser instead of uglifyjs (opt-in). UglifyJS lacks support for a lot of ES6+ features, and its cousin UglifyES is unmaintained. I've changed our pipeline configuration to use Terser instead, which is a fork of these which is actively maintained and supports newer JS. The actual dependency for this will live in @beanbag/frontend-buildkit. Some of our upstream dependencies already depend on terser, so this reduces our dependency footprint as well. I was hoping we would be able to get sourcemaps for the fully-compressed JS with this, but unfortunately the way that django-pipeline does compressors won't allow for that. # Use modern pipeline setup for static media during tests. Unit tests had their own pipeline configuration for some reason, which didn't even include any of the rollup files. This has been replaced with a call to the same `build_pipeline_settings` that we use everywhere else. # Pulled in backbone types. I'm working on upstreaming our changes and improvements to the type definitions for Backbone, but in the meantime we can't just reference the file from `node_modules` the way we did before. This has been copied into the Djblets tree for the time being. # Updated to ESLint 9. With the newer `@beanbag/eslint-plugin` bundled in `@beanbag/js-buildkit`, we now use ESLint 9. # Simplify browserslist. I've changed the browserslist config to exist inside the top-level package.json and just specify `baseline widely available`. This mirrors what we have in Review Board (though RB also has that in our shareable babel config). Testing Done: - Set up Djblets into an environment that used npm workspaces to include Djblets, Review Board, @beanbag/frontend-buildkit, @beanbag/js-buildkit, @beanbag/jasmine-suites, and @beanbag/eslint-plugin. - Ran unit tests. - Ran eslint. - Built static media. - Built the rbintegrations extension to verify that static media building still worked for that.
nqrtxwxorzwwvlnlzqmtnpuouvxxmzww
Description From Last Updated

"djblets" -> "Djblets" in the summary/description/testing. Also, the node_modules and some other references in the description should probably be code …

chipx86chipx86

This is targeting 5.x, but has breaking changes. Notably the npm_dependencies changes would break Review Board 7.0. Those would need …

chipx86chipx86

I'd like to keep r as explicit for all file operations, so it's always clear. I don't like the usage …

chipx86chipx86

5.3.

chipx86chipx86

In others, we list NotRequired below required.

chipx86chipx86

5.3.

chipx86chipx86

Blank line before return.

chipx86chipx86

Can we quote the path?

chipx86chipx86

Some people have a node_modules in $HOME. What's the risk of impact here? It may also be worth adding comments …

chipx86chipx86

This file should have a module docstring and a Version Added.

chipx86chipx86
david
david
david
chipx86
  1. 
      
  2. Show all issues

    "djblets" -> "Djblets" in the summary/description/testing.

    Also, the node_modules and some other references in the description should probably be code literals.

  3. Show all issues

    This is targeting 5.x, but has breaking changes. Notably the npm_dependencies changes would break Review Board 7.0. Those would need to be moved to a 6.0.

    1. Review Board on release-7.x depends on djblets~=5.2.1, and we have a separate release-5.2.x branch. I'm not entirely sure why we chose to do that.

      I can see a few possible ways we can fix these things up:

      1. Keep breaking changes, rename the current djblets release-5.x to be release-6.x (using merges, really), rename release-5.2.x to be release-5.x, change reviewboard release-7.x to depend on djblets~=5.x and release-7.1.x to depend on djblets~=6.x. This is probably my preference given the scale of what's already changed between release-5.2.x and release-5.x
      2. Leave reviewboard-7.x depending on djblets-5.2.x, split this change a bit to handle breaking changes (move npm_dependencies to release-6.x and add an argument flag for using terser).
      3. Some combination of 1 and 2 (deal with breaking changes and also change release-7.x to depend on djblets~=5.x)
  4. build-backend.py (Diff revision 4)
     
     
    Show all issues

    I'd like to keep r as explicit for all file operations, so it's always clear. I don't like the usage of the default for something as important as file I/O.

  5. djblets/pipeline/__init__.py (Diff revision 4)
     
     
     
    Show all issues

    5.3.

  6. djblets/pipeline/__init__.py (Diff revision 4)
     
     
     
     
     
     
     
     
     
    Show all issues

    In others, we list NotRequired below required.

  7. djblets/pipeline/settings.py (Diff revision 4)
     
     
    Show all issues

    5.3.

  8. djblets/pipeline/settings.py (Diff revision 4)
     
     
     
    Show all issues

    Blank line before return.

  9. djblets/pipeline/settings.py (Diff revision 4)
     
     
    Show all issues

    Can we quote the path?

  10. djblets/settings.py (Diff revision 4)
     
     
     
     
     
     
     
     
     
     
    Show all issues

    Some people have a node_modules in $HOME. What's the risk of impact here?

    It may also be worth adding comments above this code block to state what's going on and the implications.

    1. Shouldn't be a problem. Everything we need ought to exist inside the deeper node_modules.

  11. eslint.config.mjs (Diff revision 4)
     
     
    Show all issues

    This file should have a module docstring and a Version Added.

  12. 
      
david
david
Review request changed
Change Summary:
  • Add build-npm-deps back in and fix it up a bit.
  • Make terser opt-in with a deprecation warning if it's not opted into.
Description:
   

This change makes a bunch of changes to modernize our JavaScript build

    process across our products:

   
   

   
   

Support npm workspaces

   
   

In order to better support development across our many repos and

    packages, this dramatically improves the way we handle npm workspaces.

   
   

We used to have a djblets/package.json file which defines our JavaScript

    dependencies, and then a symbolic link from the repo root to make that
    also available for npm install. This change removes the symbolic link,
    and adds a new top-level package.json which defines workspaces as
    djblets and .npm-workspaces/*. This allows creating symbolic links
    in the workspaces directory in order to bring in development copies of
    spina, ink, beanbag-eslint-plugin, or whatever other JS dependencies one
    desires.

   
   

Depending on the package, this may require having a shared parent

    directory and a symbolic link from ../node_modules to
    ./node_modules, since some things like to resolve to absolute paths
    and then crawl up the directory tree (looking at you, tsc).

   
   

   
   

Update and consolidate JS dependencies.

   
   

The actual dependencies listed in djblets/package.json have been

    simplified, using the new @beanbag/js-buildkit metapackage to ensure
    build-time dependencies. We also no longer have an explicit dependency
    on spina and backbone versions, preferring instead to just inherit
    whichever versions we're getting from ink.

   
   

   
-  

Remove npm_dependencies and build-npm-deps.py

-  
-  

It used to be that all our JS dependencies were specified inside

-   djblets/dependencies.py and then we would write out a package.json
-   file on demand. A while back we inverted that so that package.json was
-   the authoritative list, and we would auto-generate python code back into
-   dependencies.py. However, none of that stuff was actually consumed
-   anywhere other than from within Review Board (which does the same
-   thing).

-  
-  

I've removed the build-npm-deps.py script and the associated block

-   inside djblets/dependencies.py.

-  
-  

-  
   

Use modern invocation for static media builder utilities.

   
   

We were hard-coding the locations for babel, lessc, rollup, and

    uglifyjs based on node_modules/.bin. Because of the vagaries of npm
    we can't necessarily assume that there's a node_modules in the current
    directory, or that things will be in .bin. I've changed these to use
    npm exec ... instead, allowing npm to locate the binary and invoke it.

   
   

   
~  

Use terser instead of uglifyjs.

  ~

Use terser instead of uglifyjs (opt-in).

   
   

UglifyJS lacks support for a lot of ES6+ features, and its cousin

    UglifyES is unmaintained. I've changed our pipeline configuration to use
    Terser instead, which is a fork of these which is actively maintained
    and supports newer JS. The actual dependency for this will live in
    @beanbag/frontend-buildkit.

   
   

Some of our upstream dependencies already depend on terser, so this

    reduces our dependency footprint as well.

   
   

I was hoping we would be able to get sourcemaps for the fully-compressed

    JS with this, but unfortunately the way that django-pipeline does
    compressors won't allow for that.

   
   

   
   

Use modern pipeline setup for static media during tests.

   
   

Unit tests had their own pipeline configuration for some reason, which

    didn't even include any of the rollup files. This has been replaced with
    a call to the same build_pipeline_settings that we use everywhere
    else.

   
   

   
   

Pulled in backbone types.

   
   

I'm working on upstreaming our changes and improvements to the type

    definitions for Backbone, but in the meantime we can't just reference
    the file from node_modules the way we did before. This has been copied
    into the Djblets tree for the time being.

   
   

   
   

Updated to ESLint 9.

   
   

With the newer @beanbag/eslint-plugin bundled in

    @beanbag/js-buildkit, we now use ESLint 9.

   
   

   
   

Simplify browserslist.

   
   

I've changed the browserslist config to exist inside the top-level

    package.json and just specify baseline widely available. This mirrors
    what we have in Review Board (though RB also has that in our shareable
    babel config).

Commits:
Summary ID
Modernize Djblets JavaScript setup.
This change makes a bunch of changes to modernize our JavaScript build process across our products: # Support npm workspaces In order to better support development across our many repos and packages, this dramatically improves the way we handle npm workspaces. We used to have a `djblets/package.json` file which defines our JavaScript dependencies, and then a symbolic link from the repo root to make that also available for `npm install`. This change removes the symbolic link, and adds a new top-level `package.json` which defines workspaces as `djblets` and `.npm-workspaces/*`. This allows creating symbolic links in the workspaces directory in order to bring in development copies of spina, ink, beanbag-eslint-plugin, or whatever other JS dependencies one desires. Depending on the package, this *may* require having a shared parent directory and a symbolic link from `../node_modules` to `./node_modules`, since some things like to resolve to absolute paths and then crawl up the directory tree (looking at you, `tsc`). # Update and consolidate JS dependencies. The actual dependencies listed in `djblets/package.json` have been simplified, using the new `@beanbag/js-buildkit` metapackage to ensure build-time dependencies. We also no longer have an explicit dependency on spina and backbone versions, preferring instead to just inherit whichever versions we're getting from ink. # Remove `npm_dependencies` and `build-npm-deps.py` It used to be that all our JS dependencies were specified inside `djblets/dependencies.py` and then we would write out a `package.json` file on demand. A while back we inverted that so that `package.json` was the authoritative list, and we would auto-generate python code back into `dependencies.py`. However, none of that stuff was actually consumed anywhere other than from within Review Board (which does the same thing). I've removed the `build-npm-deps.py` script and the associated block inside `djblets/dependencies.py`. # Use modern invocation for static media builder utilities. We were hard-coding the locations for `babel`, `lessc`, `rollup`, and `uglifyjs` based on `node_modules/.bin`. Because of the vagaries of npm we can't necessarily assume that there's a `node_modules` in the current directory, or that things will be in `.bin`. I've changed these to use `npm exec ...` instead, allowing npm to locate the binary and invoke it. # Use terser instead of uglifyjs. UglifyJS lacks support for a lot of ES6+ features, and its cousin UglifyES is unmaintained. I've changed our pipeline configuration to use Terser instead, which is a fork of these which is actively maintained and supports newer JS. The actual dependency for this will live in @beanbag/frontend-buildkit. Some of our upstream dependencies already depend on terser, so this reduces our dependency footprint as well. I was hoping we would be able to get sourcemaps for the fully-compressed JS with this, but unfortunately the way that django-pipeline does compressors won't allow for that. # Use modern pipeline setup for static media during tests. Unit tests had their own pipeline configuration for some reason, which didn't even include any of the rollup files. This has been replaced with a call to the same `build_pipeline_settings` that we use everywhere else. # Pulled in backbone types. I'm working on upstreaming our changes and improvements to the type definitions for Backbone, but in the meantime we can't just reference the file from `node_modules` the way we did before. This has been copied into the Djblets tree for the time being. # Updated to ESLint 9. With the newer `@beanbag/eslint-plugin` bundled in `@beanbag/js-buildkit`, we now use ESLint 9. # Simplify browserslist. I've changed the browserslist config to exist inside the top-level package.json and just specify `baseline widely available`. This mirrors what we have in Review Board (though RB also has that in our shareable babel config). Testing Done: - Set up Djblets into an environment that used npm workspaces to include Djblets, Review Board, @beanbag/frontend-buildkit, @beanbag/js-buildkit, @beanbag/jasmine-suites, and @beanbag/eslint-plugin. - Ran unit tests. - Ran eslint. - Built static media. - Built the rbintegrations extension to verify that static media building still worked for that.
nqrtxwxorzwwvlnlzqmtnpuouvxxmzww
Modernize Djblets JavaScript setup.
This change makes a bunch of changes to modernize our JavaScript build process across our products: # Support npm workspaces In order to better support development across our many repos and packages, this dramatically improves the way we handle npm workspaces. We used to have a `djblets/package.json` file which defines our JavaScript dependencies, and then a symbolic link from the repo root to make that also available for `npm install`. This change removes the symbolic link, and adds a new top-level `package.json` which defines workspaces as `djblets` and `.npm-workspaces/*`. This allows creating symbolic links in the workspaces directory in order to bring in development copies of spina, ink, beanbag-eslint-plugin, or whatever other JS dependencies one desires. Depending on the package, this *may* require having a shared parent directory and a symbolic link from `../node_modules` to `./node_modules`, since some things like to resolve to absolute paths and then crawl up the directory tree (looking at you, `tsc`). # Update and consolidate JS dependencies. The actual dependencies listed in `djblets/package.json` have been simplified, using the new `@beanbag/js-buildkit` metapackage to ensure build-time dependencies. We also no longer have an explicit dependency on spina and backbone versions, preferring instead to just inherit whichever versions we're getting from ink. # Use modern invocation for static media builder utilities. We were hard-coding the locations for `babel`, `lessc`, `rollup`, and `uglifyjs` based on `node_modules/.bin`. Because of the vagaries of npm we can't necessarily assume that there's a `node_modules` in the current directory, or that things will be in `.bin`. I've changed these to use `npm exec ...` instead, allowing npm to locate the binary and invoke it. # Use terser instead of uglifyjs (opt-in). UglifyJS lacks support for a lot of ES6+ features, and its cousin UglifyES is unmaintained. I've changed our pipeline configuration to use Terser instead, which is a fork of these which is actively maintained and supports newer JS. The actual dependency for this will live in @beanbag/frontend-buildkit. Some of our upstream dependencies already depend on terser, so this reduces our dependency footprint as well. I was hoping we would be able to get sourcemaps for the fully-compressed JS with this, but unfortunately the way that django-pipeline does compressors won't allow for that. # Use modern pipeline setup for static media during tests. Unit tests had their own pipeline configuration for some reason, which didn't even include any of the rollup files. This has been replaced with a call to the same `build_pipeline_settings` that we use everywhere else. # Pulled in backbone types. I'm working on upstreaming our changes and improvements to the type definitions for Backbone, but in the meantime we can't just reference the file from `node_modules` the way we did before. This has been copied into the Djblets tree for the time being. # Updated to ESLint 9. With the newer `@beanbag/eslint-plugin` bundled in `@beanbag/js-buildkit`, we now use ESLint 9. # Simplify browserslist. I've changed the browserslist config to exist inside the top-level package.json and just specify `baseline widely available`. This mirrors what we have in Review Board (though RB also has that in our shareable babel config). Testing Done: - Set up Djblets into an environment that used npm workspaces to include Djblets, Review Board, @beanbag/frontend-buildkit, @beanbag/js-buildkit, @beanbag/jasmine-suites, and @beanbag/eslint-plugin. - Ran unit tests. - Ran eslint. - Built static media. - Built the rbintegrations extension to verify that static media building still worked for that.
nqrtxwxorzwwvlnlzqmtnpuouvxxmzww

Checks run (2 succeeded)

flake8 passed.
JSHint passed.