Skip to content

Commit 4f508b1

Browse files
committed
feat: support bazel+js packages that install into regular @npm//package:index.bzl location
1 parent 2e93d96 commit 4f508b1

10 files changed

Lines changed: 155 additions & 18 deletions

File tree

internal/npm_install/generate_build_file.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,24 +186,63 @@ node_module_library(
186186
* Generates all BUILD & bzl files for a package.
187187
*/
188188
function generatePackageBuildFiles(pkg: Dep) {
189+
// If a BUILD file was shipped with the package, append its contents to the end of
190+
// what we generate for the package.
191+
let buildFilePath: string|undefined;
192+
if (pkg._files.includes('BUILD')) buildFilePath = 'BUILD';
193+
if (pkg._files.includes('BUILD.bazel')) buildFilePath = 'BUILD.bazel';
189194
let buildFile = printPackage(pkg);
195+
if (buildFilePath) {
196+
buildFile = buildFile + '\n' +
197+
fs.readFileSync(path.join('node_modules', pkg._dir, buildFilePath), 'utf-8');
198+
} else {
199+
buildFilePath = 'BUILD.bazel'
200+
}
190201

191-
const binBuildFile = printPackageBin(pkg);
192-
if (binBuildFile.length) {
193-
writeFileSync(
194-
path.posix.join(pkg._dir, 'bin', 'BUILD.bazel'), BUILD_FILE_HEADER + binBuildFile);
202+
// If the package didn't ship a bin/BUILD file, generate one.
203+
if (!pkg._files.includes('bin/BUILD.bazel') && !pkg._files.includes('bin/BUILD')) {
204+
const binBuildFile = printPackageBin(pkg);
205+
if (binBuildFile.length) {
206+
writeFileSync(
207+
path.posix.join(pkg._dir, 'bin', 'BUILD.bazel'), BUILD_FILE_HEADER + binBuildFile);
208+
}
195209
}
196210

197-
const indexFile = printIndexBzl(pkg);
198-
if (indexFile.length) {
199-
writeFileSync(path.posix.join(pkg._dir, 'index.bzl'), indexFile);
200-
buildFile = `${buildFile}
211+
// If there's an index.bzl in the package then copy all the package's files
212+
// other than the BUILD file which we'll write below.
213+
// (maybe we shouldn't copy .js though, since it belongs under node_modules?)
214+
if (pkg._files.includes('index.bzl')) {
215+
pkg._files.filter(f => f !== 'BUILD' && f !== 'BUILD.bazel').forEach(file => {
216+
if (/^node_modules[/\\]/.test(file)) {
217+
// don't copy over nested node_modules
218+
return;
219+
}
220+
// don't support rootPath here?
221+
let destFile = path.posix.join(pkg._dir, file);
222+
const basename = path.basename(file);
223+
const basenameUc = basename.toUpperCase();
224+
// Bazel BUILD files from npm distribution would have been renamed earlier with a _ prefix so
225+
// we restore the name on the copy
226+
if (basenameUc === '_BUILD' || basenameUc === '_BUILD.BAZEL') {
227+
destFile = path.posix.join(path.dirname(destFile), basename.substr(1));
228+
}
229+
const src = path.posix.join('node_modules', pkg._dir, file);
230+
231+
mkdirp(path.dirname(destFile));
232+
fs.copyFileSync(src, destFile);
233+
});
234+
} else {
235+
const indexFile = printIndexBzl(pkg);
236+
if (indexFile.length) {
237+
writeFileSync(path.posix.join(pkg._dir, 'index.bzl'), indexFile);
238+
buildFile += `
201239
# For integration testing
202240
exports_files(["index.bzl"])
203241
`;
242+
}
204243
}
205244

206-
writeFileSync(path.posix.join(pkg._dir, 'BUILD.bazel'), BUILD_FILE_HEADER + buildFile);
245+
writeFileSync(path.posix.join(pkg._dir, buildFilePath), BUILD_FILE_HEADER + buildFile);
207246
}
208247

209248
/**

internal/npm_install/index.js

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,52 @@ node_module_library(
102102
writeFileSync('BUILD.bazel', buildFile);
103103
}
104104
function generatePackageBuildFiles(pkg) {
105+
let buildFilePath;
106+
if (pkg._files.includes('BUILD'))
107+
buildFilePath = 'BUILD';
108+
if (pkg._files.includes('BUILD.bazel'))
109+
buildFilePath = 'BUILD.bazel';
105110
let buildFile = printPackage(pkg);
106-
const binBuildFile = printPackageBin(pkg);
107-
if (binBuildFile.length) {
108-
writeFileSync(path.posix.join(pkg._dir, 'bin', 'BUILD.bazel'), BUILD_FILE_HEADER + binBuildFile);
109-
}
110-
const indexFile = printIndexBzl(pkg);
111-
if (indexFile.length) {
112-
writeFileSync(path.posix.join(pkg._dir, 'index.bzl'), indexFile);
113-
buildFile = `${buildFile}
111+
if (buildFilePath) {
112+
buildFile = buildFile + '\n' +
113+
fs.readFileSync(path.join('node_modules', pkg._dir, buildFilePath), 'utf-8');
114+
}
115+
else {
116+
buildFilePath = 'BUILD.bazel';
117+
}
118+
if (!pkg._files.includes('bin/BUILD.bazel') && !pkg._files.includes('bin/BUILD')) {
119+
const binBuildFile = printPackageBin(pkg);
120+
if (binBuildFile.length) {
121+
writeFileSync(path.posix.join(pkg._dir, 'bin', 'BUILD.bazel'), BUILD_FILE_HEADER + binBuildFile);
122+
}
123+
}
124+
if (pkg._files.includes('index.bzl')) {
125+
pkg._files.filter(f => f !== 'BUILD' && f !== 'BUILD.bazel').forEach(file => {
126+
if (/^node_modules[/\\]/.test(file)) {
127+
return;
128+
}
129+
let destFile = path.posix.join(pkg._dir, file);
130+
const basename = path.basename(file);
131+
const basenameUc = basename.toUpperCase();
132+
if (basenameUc === '_BUILD' || basenameUc === '_BUILD.BAZEL') {
133+
destFile = path.posix.join(path.dirname(destFile), basename.substr(1));
134+
}
135+
const src = path.posix.join('node_modules', pkg._dir, file);
136+
mkdirp(path.dirname(destFile));
137+
fs.copyFileSync(src, destFile);
138+
});
139+
}
140+
else {
141+
const indexFile = printIndexBzl(pkg);
142+
if (indexFile.length) {
143+
writeFileSync(path.posix.join(pkg._dir, 'index.bzl'), indexFile);
144+
buildFile += `
114145
# For integration testing
115146
exports_files(["index.bzl"])
116147
`;
148+
}
117149
}
118-
writeFileSync(path.posix.join(pkg._dir, 'BUILD.bazel'), BUILD_FILE_HEADER + buildFile);
150+
writeFileSync(path.posix.join(pkg._dir, buildFilePath), BUILD_FILE_HEADER + buildFile);
119151
}
120152
function generateBazelWorkspaces(pkgs) {
121153
const workspaces = {};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
load("@npm//bazel_workspaces_consistent:index.bzl", "some_rule")
2+
load("@npm_bazel_jasmine//:index.bzl", "jasmine_node_test")
3+
4+
some_rule(name = "test_data")
5+
6+
jasmine_node_test(
7+
name = "test",
8+
srcs = ["spec.js"],
9+
data = ["test_data"],
10+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
These test the installation of npm packages which contain bazel rules.
2+
We call these "hybrid" packages because they're distributed, versioned, and installed by npm
3+
but they contain bazel rules we can call from BUILD files.
4+
5+
The packages themselves are in /tools/npm_packages/bazel_workspaces*
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
describe('installing hybrid packages', () => {
5+
it('should work', () => {
6+
const content = fs.readFileSync(
7+
path.join(process.env['TEST_SRCDIR'], 'npm', 'bazel_workspaces_consistent', 'a.txt'),
8+
'utf-8');
9+
expect(content).toEqual('some content');
10+
});
11+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@types/semver": "6.2.0",
3232
"babel-jest": "^25.5.1",
3333
"bazel_workspaces": "file:./tools/npm_packages/bazel_workspaces",
34+
"bazel_workspaces_consistent": "file:./tools/npm_packages/bazel_workspaces_consistent",
3435
"clang-format": "1.2.2",
3536
"conventional-changelog-cli": "^2.0.21",
3637
"core-util-is": "^1.0.2",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel-skylib:rules/write_file.bzl", "write_file")
2+
load(":index.bzl", "some_rule")
3+
4+
# Just a dumb target to make sure we can use it from code that installs this npm package
5+
write_file(
6+
name = "some_file",
7+
out = "a.txt",
8+
content = ["some content"],
9+
visibility = ["//visibility:public"],
10+
)
11+
12+
some_rule(
13+
name = "test",
14+
# Normally we would set the default to work in our source repo,
15+
# and transform on publish.
16+
text = "//tools/npm_packages/bazel_workspaces_consistent:a.txt",
17+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"Simplest possible rule for testing it can be loaded and called"
2+
3+
_ATTRS = {
4+
# Note, we can reference our file without needing "@npm" which means it works
5+
# regardless what name the user chooses for their workspace
6+
"text": attr.label(default = Label("//bazel_workspaces_consistent:a.txt"), allow_single_file = True),
7+
}
8+
9+
def _impl(ctx):
10+
# No actions, just echo the input file as the default output
11+
return [DefaultInfo(files = depset(ctx.files.text))]
12+
13+
some_rule = rule(_impl, attrs = _ATTRS)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "bazel_workspaces_consistent",
3+
"version": "0.0.1",
4+
"description": "https://hackmd.io/JkUESy8JTkyvGIlc-vNjeQ"
5+
}
6+

yarn.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,9 @@ base@^0.11.1:
22202220
"bazel_workspaces@file:./tools/npm_packages/bazel_workspaces":
22212221
version "0.0.2"
22222222

2223+
"bazel_workspaces_consistent@file:./tools/npm_packages/bazel_workspaces_consistent":
2224+
version "0.0.1"
2225+
22232226
bcrypt-pbkdf@^1.0.0:
22242227
version "1.0.2"
22252228
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"

0 commit comments

Comments
 (0)