Skip to content

Commit

Permalink
Introduce npm_package rule
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeagle committed Feb 16, 2018
1 parent befbd89 commit 7a8e757
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 1 deletion.
2 changes: 2 additions & 0 deletions defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ load("//internal/jasmine_node_test:jasmine_node_test.bzl", _jasmine_node_test =
load("//internal/npm_install:npm_install.bzl", _npm_install = "npm_install")
load("//internal/yarn_install:yarn_install.bzl", _yarn_install = "yarn_install")
load("//internal/rollup:rollup_bundle.bzl", _rollup_bundle = "rollup_bundle")
load("//internal/npm_package:npm_package.bzl", _npm_package = "npm_package")

check_bazel_version = _check_bazel_version
nodejs_binary = _nodejs_binary
Expand All @@ -35,3 +36,4 @@ jasmine_node_test = _jasmine_node_test
npm_install = _npm_install
yarn_install = _yarn_install
rollup_bundle = _rollup_bundle
npm_package = _npm_package
12 changes: 11 additions & 1 deletion internal/node/node_repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,21 @@ def _node_impl(repository_ctx):
npm = "bin/npm"
repository_ctx.file("BUILD.bazel", content="""#Generated by node_repositories.bzl
package(default_visibility = ["//visibility:public"])
exports_files(["{0}"])
exports_files(["{0}", "run_npm.sh.template"])
alias(name = "node", actual = "{0}")
sh_binary(
name = "npm",
srcs = ["npm.sh"],
)
""".format(node))

# `yarn publish` is not ready for use under Bazel, see https://github.com/yarnpkg/yarn/issues/610
repository_ctx.file("run_npm.sh.template", content="""
NODE="{}"
NPM="{}"
"$NODE" "$NPM" TMPL_args "$@"
""".format(repository_ctx.path(node), repository_ctx.path(npm)))

repository_ctx.file("npm.sh", content="""#!/bin/bash
#Generated by node_repositories.bzl
""" + "".join(["""
Expand Down Expand Up @@ -109,12 +117,14 @@ def _yarn_impl(repository_ctx):
# speed and correctness. We download a specific version of Yarn to ensure a hermetic build.
repository_ctx.file("BUILD.bazel", content="""#Generated by node_repositories.bzl
package(default_visibility = ["//visibility:public"])
exports_files(["bin/yarn.js"])
sh_binary(
name = "yarn",
srcs = ["yarn.sh"],
)
""")
node = get_node_label(repository_ctx)

repository_ctx.file("yarn.sh", content="""#!/bin/bash
#Generated by node_repositories.bzl
""" + "".join(["""
Expand Down
11 changes: 11 additions & 0 deletions internal/npm_package/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")

nodejs_binary(
name = "packager",
data = [
"packager.js",
"@nodejs//:run_npm.sh.template",
],
entry_point = "build_bazel_rules_nodejs/internal/npm_package/packager.js",
visibility = ["//visibility:public"],
)
75 changes: 75 additions & 0 deletions internal/npm_package/npm_package.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""The npm_package rule creates a directory containing a publishable npm artifact.
It also produces two named outputs:
:label.pack
:label.publish
These can be used with `bazel run` to create a .tgz of the package and to publish
the package to the npm registry, respectively.
"""

load("//internal:node.bzl", "sources_aspect")

def create_package(ctx, devmode_sources):
"""Creates an action that produces the npm package.
It copies srcs and deps into the artifact and produces the .pack and .publish
scripts.
Args:
ctx: the skylark rule context
devmode_sources: the .js files which belong in the package
Returns:
The tree artifact which is the publishable directory.
"""

package_dir = ctx.actions.declare_directory(ctx.label.name)

args = ctx.actions.args()
args.add(package_dir.path)
args.add([s.path for s in ctx.files.srcs], join_with=",")
args.add(ctx.bin_dir.path)
args.add([s.path for s in devmode_sources], join_with=",")
args.add([ctx.outputs.pack.path, ctx.outputs.publish.path])

ctx.action(
executable = ctx.executable._packager,
inputs = ctx.files.srcs + devmode_sources + [ctx.file._run_npm_template],
outputs = [package_dir, ctx.outputs.pack, ctx.outputs.publish],
arguments = [args],
)
return package_dir

def _npm_package(ctx):
files = depset()
for d in ctx.attr.deps:
files = depset(transitive = [files, d.files, d.node_sources])

package_dir = create_package(ctx, files.to_list())

return [DefaultInfo(
files = depset([package_dir]),
)]

NPM_PACKAGE_ATTRS = {
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(aspects = [sources_aspect]),
"_packager": attr.label(
default = Label("//internal/npm_package:packager"),
cfg = "host", executable = True),
"_run_npm_template": attr.label(
default = Label("@nodejs//:run_npm.sh.template"),
allow_single_file = True),
}

NPM_PACKAGE_OUTPUTS = {
"pack": "%{name}.pack",
"publish": "%{name}.publish",
}

npm_package = rule(
implementation = _npm_package,
attrs = NPM_PACKAGE_ATTRS,
outputs = NPM_PACKAGE_OUTPUTS,
)
61 changes: 61 additions & 0 deletions internal/npm_package/packager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @license
* Copyright 2018 The Bazel Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');

function mkdirp(p) {
if (!fs.existsSync(p)) {
mkdirp(path.dirname(p));
fs.mkdirSync(p);
}
}

function write(p, content) {
mkdirp(path.dirname(p));
fs.writeFileSync(p, content);
}

// TODO(alexeagle): add support for version stamping, we might want to replace
// the version number in some of the files (eg. take the latest git tag and
// overwrite the version in package.json)

function main(args) {
const [outDir, srcsArg, binDir, depsArg, packPath, publishPath] = args;

// src like my/path is just copied to outDir/my/path
for (src of srcsArg.split(',').filter(s => !!s)) {
const content = fs.readFileSync(src, {encoding: 'utf-8'});
const outPath = path.join(outDir, src);
write(outPath, content);
}

// deps like bazel-bin/my/path is copied to outDir/my/path
for (dep of depsArg.split(',').filter(s => !!s)) {
const content = fs.readFileSync(dep, {encoding: 'utf-8'})
const outPath = path.join(outDir, path.relative(binDir, dep));
write(outPath, content);
}

const npmTemplate =
fs.readFileSync(require.resolve('nodejs/run_npm.sh.template'), {encoding: 'utf-8'});
fs.writeFileSync(packPath, npmTemplate.replace('TMPL_args', `pack ${outDir}`));
fs.writeFileSync(publishPath, npmTemplate.replace('TMPL_args', `publish ${outDir}`));
}

if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}

0 comments on commit 7a8e757

Please sign in to comment.