From b43b164a61a76b9531b9f6a8b217ecb48d6d2deb Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Tue, 13 Feb 2018 11:26:06 -0800 Subject: [PATCH] feat(bazel): add an ng_package rule (#22221) This produces a directory following the Angular Package layout spec. Includes integration test coverage by making a minimal ng_package in integration/bazel. Unit tests verify the content of the @angular/core and @angular/common packages. This doesn't totally match our current output, but is good enough to unblock some early adopters. It re-uses logic from the rollup_bundle rule in rules_nodejs. It should also eventually have the .pack and .publish secondary targets like npm_package rule. PR Close #22221 --- WORKSPACE | 6 +- integration/bazel/BUILD.bazel | 32 +- integration/bazel/WORKSPACE | 10 +- integration/bazel/src/hello-world/BUILD.bazel | 8 +- integration/bazel/src/hello-world/index.ts | 2 + integration/bazel/yarn.lock | 282 ++++++++++++++++-- package.json | 1 + packages/bazel/index.bzl | 2 + packages/bazel/package.json | 9 +- packages/bazel/src/ng_module.bzl | 17 +- packages/bazel/src/ng_package/BUILD.bazel | 18 ++ packages/bazel/src/ng_package/ng_package.bzl | 229 ++++++++++++++ packages/bazel/src/ng_package/packager.ts | 141 +++++++++ packages/bazel/src/ng_package/tsconfig.json | 5 + packages/bazel/test/ng_package/BUILD.bazel | 36 +++ .../test/ng_package/common_package.spec.ts | 50 ++++ .../test/ng_package/core_package.spec.ts | 227 ++++++++++++++ packages/common/BUILD.bazel | 18 +- .../bazel/ng_module/BUILD.bazel | 1 + packages/core/BUILD.bazel | 13 +- packages/core/testing/BUILD.bazel | 9 +- tools/defaults.bzl | 23 +- yarn.lock | 23 ++ 23 files changed, 1101 insertions(+), 61 deletions(-) create mode 100644 integration/bazel/src/hello-world/index.ts create mode 100644 packages/bazel/src/ng_package/BUILD.bazel create mode 100644 packages/bazel/src/ng_package/ng_package.bzl create mode 100644 packages/bazel/src/ng_package/packager.ts create mode 100644 packages/bazel/src/ng_package/tsconfig.json create mode 100644 packages/bazel/test/ng_package/BUILD.bazel create mode 100644 packages/bazel/test/ng_package/common_package.spec.ts create mode 100644 packages/bazel/test/ng_package/core_package.spec.ts diff --git a/WORKSPACE b/WORKSPACE index 55a20023f5..e6a50b61ec 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,14 +1,14 @@ workspace(name = "angular") # Using a pre-release snapshot to pick up a commit that makes all nodejs_binary -# programs produce source-mapped stack traces. -RULES_NODEJS_VERSION = "926349cea4cd360afcd5647ccdd09d2d2fb471aa" +# programs produce source-mapped stack traces and uglify sourcemaps. +RULES_NODEJS_VERSION = "4303cbef12e5e252ad66cc35cff1123e3a44ee83" http_archive( name = "build_bazel_rules_nodejs", url = "https://github.com/bazelbuild/rules_nodejs/archive/%s.zip" % RULES_NODEJS_VERSION, strip_prefix = "rules_nodejs-%s" % RULES_NODEJS_VERSION, - sha256 = "5ba3c8c209078c2e3f0c6aa4abd01a1a561f92a5bfda04e25604af5f4734d69d", + sha256 = "fccb9a7122f339d89c9994dc0fea33c737dd76e66281d0da0cb841da5f1edec7", ) load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories") diff --git a/integration/bazel/BUILD.bazel b/integration/bazel/BUILD.bazel index 23dee7adc6..2a800b8b5c 100644 --- a/integration/bazel/BUILD.bazel +++ b/integration/bazel/BUILD.bazel @@ -2,25 +2,15 @@ package(default_visibility = ["//visibility:public"]) filegroup( name = "node_modules", - # Workaround https://github.com/bazelbuild/bazel/issues/4242 - # Can't just glob(["node_modules/**/*.{js,d.ts,json}"]) - srcs = glob(["/".join([ - "node_modules", - pkg, - "**", - ext, - ]) for pkg in [ - "@angular", - "@types", - "bytebuffer", - "protobufjs", - "reflect-metadata", - "tsickle", - "typescript", - "zone.js", - ] for ext in [ - "*.js", - "*.json", - "*.d.ts", - ]]), + srcs = glob( + ["node_modules/**/*"], + # Exclude directories that commonly contain filenames which are + # illegal bazel labels + exclude = [ + # e.g. node_modules/adm-zip/test/assets/attributes_test/New folder/hidden.txt + "node_modules/**/test/**", + # e.g. node_modules/xpath/docs/function resolvers.md + "node_modules/**/docs/**", + ], + ), ) diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index f4e3674bbb..c680a8b74e 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -1,10 +1,14 @@ workspace(name = "bazel_integration_test") +# Using a pre-release snapshot to pick up a commit that makes all nodejs_binary +# programs produce source-mapped stack traces and uglify sourcemaps. +RULES_NODEJS_VERSION = "4303cbef12e5e252ad66cc35cff1123e3a44ee83" + http_archive( name = "build_bazel_rules_nodejs", - url = "https://github.com/bazelbuild/rules_nodejs/archive/0.4.1.zip", - strip_prefix = "rules_nodejs-0.4.1", - sha256 = "e9bc013417272b17f302dc169ad597f05561bb277451f010043f4da493417607", + url = "https://github.com/bazelbuild/rules_nodejs/archive/%s.zip" % RULES_NODEJS_VERSION, + strip_prefix = "rules_nodejs-%s" % RULES_NODEJS_VERSION, + sha256 = "fccb9a7122f339d89c9994dc0fea33c737dd76e66281d0da0cb841da5f1edec7", ) load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories") diff --git a/integration/bazel/src/hello-world/BUILD.bazel b/integration/bazel/src/hello-world/BUILD.bazel index 496922e6ee..09a8465851 100644 --- a/integration/bazel/src/hello-world/BUILD.bazel +++ b/integration/bazel/src/hello-world/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@angular//:index.bzl", "ng_module") +load("@angular//:index.bzl", "ng_module", "ng_package") load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") sass_binary( @@ -17,3 +17,9 @@ ng_module( # npm distro of angular there is no ts_library rule to propagate the dep. deps = ["@rxjs"], ) + +ng_package( + name = "npm_package", + entry_point = "src/hello-world/index.js", + deps = [":hello-world"], +) diff --git a/integration/bazel/src/hello-world/index.ts b/integration/bazel/src/hello-world/index.ts new file mode 100644 index 0000000000..3362a036e5 --- /dev/null +++ b/integration/bazel/src/hello-world/index.ts @@ -0,0 +1,2 @@ +export * from './hello-world.component'; +export * from './hello-world.module'; diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index f81ba79a2d..f820ad15aa 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,47 +3,79 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: tslib "^1.7.1" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: "@types/node" "6.0.84" + "@types/shelljs" "0.7.7" + protobufjs "5.0.0" + rollup "0.47.4" + shelljs "0.7.8" + uglify-js "2.8.29" "@angular/common@file:../../dist/packages-dist/common": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: tslib "^1.7.1" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: chokidar "^1.4.2" minimist "^1.2.0" reflect-metadata "^0.1.2" - tsickle "^0.26.0" + tsickle "^0.27.2" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: tslib "^1.7.1" "@angular/core@file:../../dist/packages-dist/core": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: tslib "^1.7.1" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "5.2.0-e94fb16a2" + version "6.0.0-beta.4-7f6fcc1cc" dependencies: tslib "^1.7.1" +"@types/events@*": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-1.1.0.tgz#93b1be91f63c184450385272c47b6496fd028e02" + +"@types/glob@*": + version "5.0.35" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + +"@types/node@*": + version "9.4.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e" + "@types/node@6.0.84": version "6.0.84" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.84.tgz#193ffe5a9f42864d425ffd9739d95b753c6a1eab" +"@types/shelljs@0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.7.tgz#1f7bfa28947661afea06365db9b1135bbc903ec4" + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/source-map@0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@types/source-map/-/source-map-0.5.1.tgz#7e74db5d06ab373a712356eebfaea2fad0ea2367" @@ -59,6 +91,14 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -95,6 +135,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +ascli@~1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc" + dependencies: + colour "~0.7.1" + optjs "~3.2.2" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -164,10 +211,31 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +bytebuffer@~5: + version "5.0.1" + resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" + dependencies: + long "~3" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + chokidar@^1.4.2: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -183,6 +251,22 @@ chokidar@^1.4.2: optionalDependencies: fsevents "^1.0.0" +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.0.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -191,6 +275,10 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +colour@~0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -227,6 +315,10 @@ debug@^2.2.0: dependencies: ms "2.0.0" +decamelize@^1.0.0, decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -375,7 +467,17 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^7.0.5: +glob@^5.0.10: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.5: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -441,6 +543,14 @@ ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" +interpret@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -562,6 +672,24 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +long@~3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -590,7 +718,7 @@ mime-types@^2.1.12, mime-types@~2.1.7: dependencies: mime-db "~1.30.0" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -681,10 +809,20 @@ once@^1.3.0, once@^1.3.3: dependencies: wrappy "1" +optjs@~3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee" + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -709,6 +847,10 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" @@ -721,6 +863,15 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" +protobufjs@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.0.tgz#4223063233ea96ac063ca2b554035204db524fa1" + dependencies: + ascli "~1" + bytebuffer "~5" + glob "^5.0.10" + yargs "^3.10.0" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -766,6 +917,12 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + reflect-metadata@^0.1.2: version "0.1.10" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a" @@ -815,12 +972,28 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +resolve@^1.1.6: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" +rollup@0.47.4: + version "0.47.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.47.4.tgz#e3a55de83a78221d232ce29619a8d68189ae845e" + "rxjs@file:../../node_modules/rxjs": version "5.5.5" dependencies: @@ -842,6 +1015,14 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +shelljs@0.7.8: + version "0.7.8" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -852,13 +1033,17 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -source-map-support@^0.4.2: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" +source-map-support@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76" dependencies: - source-map "^0.5.6" + source-map "^0.6.0" -source-map@^0.5.6: +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -935,14 +1120,14 @@ tough-cookie@~2.3.0: dependencies: punycode "^1.4.1" -tsickle@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.26.0.tgz#40b30a2dd6abcb33b182e37596674bd1cfe4039c" +tsickle@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736" dependencies: minimist "^1.2.0" mkdirp "^0.5.1" - source-map "^0.5.6" - source-map-support "^0.4.2" + source-map "^0.6.0" + source-map-support "^0.5.0" tslib@^1.7.1: version "1.8.1" @@ -961,6 +1146,19 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: "typescript@file:../../node_modules/typescript": version "2.6.2" +uglify-js@2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -987,9 +1185,53 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +window-size@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +y18n@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yargs@^3.10.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + dependencies: + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + "zone.js@file:../../node_modules/zone.js": version "0.8.17" diff --git a/package.json b/package.json index b86cff3a4a..e6c522895e 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@types/jasmine": "2.2.22-alpha", "@types/node": "6.0.88", "@types/selenium-webdriver": "3.0.7", + "@types/shelljs": "^0.7.8", "@types/source-map": "^0.5.1", "@types/systemjs": "0.19.32", "angular": "1.5.0", diff --git a/packages/bazel/index.bzl b/packages/bazel/index.bzl index 62337e440d..33cefabcda 100644 --- a/packages/bazel/index.bzl +++ b/packages/bazel/index.bzl @@ -8,5 +8,7 @@ Users should not load files under "/src" """ load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module") +load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package") ng_module = _ng_module +ng_package = _ng_package diff --git a/packages/bazel/package.json b/packages/bazel/package.json index 0e959b039d..c92d00df30 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -4,13 +4,16 @@ "description": "Angular - bazel build rules", "author": "angular", "license": "MIT", + "dependencies": { + "@types/node": "6.0.84", + "@types/shelljs": "0.7.7", + "shelljs": "0.7.8", + "protobufjs": "5.0.0" + }, "peerDependencies": { "@angular/compiler-cli": "0.0.0-PLACEHOLDER", "typescript": ">=2.4.2 <2.7" }, - "dependencies": { - "@types/node": "6.0.84" - }, "repository": { "type": "git", "url": "https://github.com/angular/angular.git" diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index b36636c09f..bcc9533c87 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -35,8 +35,10 @@ def _expected_outs(ctx): factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories]) for src in ctx.files.srcs + ctx.files.assets: + package_prefix = ctx.label.package + "/" if ctx.label.package else "" + if src.short_path.endswith(".ts") and not src.short_path.endswith(".d.ts"): - basename = src.short_path[len(ctx.label.package) + 1:-len(".ts")] + basename = src.short_path[len(package_prefix):-len(".ts")] if len(factory_basename_set) == 0 or basename in factory_basename_set: devmode_js = [ ".ngfactory.js", @@ -48,7 +50,7 @@ def _expected_outs(ctx): devmode_js = [".js"] summaries = [] elif src.short_path.endswith(".css"): - basename = src.short_path[len(ctx.label.package) + 1:-len(".css")] + basename = src.short_path[len(package_prefix):-len(".css")] devmode_js = [ ".css.shim.ngstyle.js", ".css.ngstyle.js", @@ -252,15 +254,19 @@ def _write_bundle_index(ctx): if ctx.attr.module_name: tsconfig["angularCompilerOptions"]["flatModuleId"] = ctx.attr.module_name + entry_point = ctx.attr.entry_point if ctx.attr.entry_point else "index.ts" # createBundleIndexHost in bundle_index_host.ts will throw if the "files" has more than one entry. # We don't want to fail() here, however, because not all ng_module's will have the bundle index written. # So we make the assumption that the index.ts file in the highest parent directory is the entry point. index_file = None + for f in tsconfig["files"]: - if f.endswith("/index.ts"): + if f.endswith("/" + entry_point): if not index_file or len(f) < len(index_file): index_file = f - tsconfig["files"] = [index_file] + + if index_file: + tsconfig["files"] = [index_file] ctx.actions.write(tsconfig_file, json_marshal(tsconfig)) @@ -361,6 +367,9 @@ ng_module = rule( "node_modules": attr.label( default = Label("@//:node_modules") ), + + "entry_point": attr.string(), + "_index_bundler": attr.label( executable = True, cfg = "host", diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel new file mode 100644 index 0000000000..41f2782aff --- /dev/null +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -0,0 +1,18 @@ +package(default_visibility = ["//visibility:public"]) + +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + +exports_files(["rollup.config.js"]) + +ts_library( + name = "lib", + srcs = glob(["*.ts"]), + tsconfig = ":tsconfig.json", +) + +nodejs_binary( + name = "packager", + data = ["lib"], + entry_point = "angular/packages/bazel/src/ng_package/packager.js", +) diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl new file mode 100644 index 0000000000..9c2d89809b --- /dev/null +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -0,0 +1,229 @@ +# Copyright Google Inc. All Rights Reserved. +# +# Use of this source code is governed by an MIT-style license that can be +# found in the LICENSE file at https://angular.io/license +"""Implementation of the ng_package rule. +""" + +load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es6_sources") +load("@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl", + "write_rollup_config", + "rollup_module_mappings_aspect", + "run_uglify", + "ROLLUP_ATTRS") +load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "ESM5Info") + +# TODO(alexeagle): this list is incomplete, add more as material ramps up +WELL_KNOWN_GLOBALS = { + "@angular/core": "ng.core", + "@angular/common": "ng.common", + "@angular/platform-browser": "ng.platformBrowser", + "rxjs/Observable": "Rx", + "rxjs/Observer": "Rx", + "rxjs/Subject": "Rx", + "rxjs/Subscription": "Rx", + "rxjs/observable/merge": "Rx.Observable", + "rxjs/observable/of": "Rx.Observable.prototype", + "rxjs/operator/concatMap": "Rx.Observable.prototype", + "rxjs/operator/filter": "Rx.Observable.prototype", + "rxjs/operator/map": "Rx.Observable.prototype", + "rxjs/operator/share": "Rx.Observable.prototype", +} + +def _rollup(ctx, rollup_config, entry_point, inputs, js_output, format = "es"): + map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output) + + args = ctx.actions.args() + args.add(["--config", rollup_config.path]) + + args.add(["--input", entry_point]) + args.add(["--output.file", js_output.path]) + args.add(["--output.format", format]) + args.add(["--name", ctx.label.name]) + + # Note: if the input has external source maps then we need to also install and use + # `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead + # of command line args + args.add("--sourcemap") + + globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals) + externals = globals.keys() + args.add("--external") + args.add(externals, join_with=",") + + other_inputs = [ctx.executable._rollup, rollup_config] + if ctx.file.stamp_data: + other_inputs.append(ctx.file.stamp_data) + if ctx.file.license_banner: + other_inputs.append(ctx.file.license_banner) + ctx.actions.run( + progress_message = "Angular Packaging: rolling up %s" % ctx.label.name, + mnemonic = "AngularPackageRollup", + inputs = inputs.to_list() + other_inputs, + outputs = [js_output, map_output], + executable = ctx.executable._rollup, + arguments = [args], + ) + return struct( + js = js_output, + map = map_output, + ) + +# convert from [{js: js_file1, map: map_file1}, ...] to +# [js_filepath1, map_filepath1, ...] +def _flatten_paths(directory): + result = [] + for f in directory: + result.extend([f.js.path, f.map.path]) + return result + +# ng_package produces package that is npm-ready. +def _ng_package_impl(ctx): + npm_package_directory = ctx.actions.declare_directory(ctx.label.name) + + esm_2015_files = collect_es6_sources(ctx) + + esm5_sources = depset() + root_dirs = [] + + for dep in ctx.attr.deps: + if ESM5Info in dep: + # TODO(alexeagle): we could make the module resolution in the rollup plugin + # faster if we kept the files grouped with their root dir. This approach just + # passes in both lists and requires multiple lookups (with expensive exception + # handling) to locate the files again. + transitive_output = dep[ESM5Info].transitive_output + root_dirs.extend(transitive_output.keys()) + esm5_sources = depset(transitive=[esm5_sources] + transitive_output.values()) + + # These accumulators match the directory names where the files live in the + # Angular package format. + esm2015 = [] + esm5 = [] + bundles = [] + + for entry_point in [''] + ctx.attr.secondary_entry_points: + es2015_entry_point = "/".join([p for p in [ + ctx.bin_dir.path, + ctx.label.package, + ctx.label.name + ".es6", + ctx.label.package, + entry_point, + "index.js", + ] if p]) + + es5_entry_point = "/".join([p for p in [ + ctx.label.package, + entry_point, + "index.js", + ] if p]) + + if entry_point: + # TODO jasonaden says there is no particular reason these filenames differ + umd_output_filename = "-".join([ctx.label.package.split("/")[-1]] + entry_point.split("/")) + fesm_output_filename = entry_point.replace("/", "__") + fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename) + fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename) + umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename) + min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename) + else: + fesm2015_output = ctx.outputs.fesm2015 + fesm5_output = ctx.outputs.fesm5 + umd_output = ctx.outputs.umd + min_output = ctx.outputs.umd_min + + config = write_rollup_config(ctx, [], root_dirs) + esm2015.append(_rollup(ctx, config, es2015_entry_point, esm_2015_files, fesm2015_output)) + esm5.append(_rollup(ctx, config, es5_entry_point, esm5_sources, fesm5_output)) + bundles.append(_rollup(ctx, config, es5_entry_point, esm5_sources, umd_output, format = "umd")) + uglify_sourcemap = run_uglify(ctx, umd_output, min_output, + config_name = entry_point.replace("/", "_")) + bundles.append(struct(js = min_output, map = uglify_sourcemap)) + + metadata_files = depset(transitive = [getattr(dep, "angular").flat_module_metadata + for dep in ctx.attr.deps + if hasattr(dep, "angular")]) + + # TODO: the args look long, maybe need to spill to a params file: + # https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file + args = ctx.actions.args() + args.add(npm_package_directory.path) + args.add(ctx.label.package) + args.add([ctx.bin_dir.path, ctx.label.package], join_with="/") + args.add(ctx.file.readme_md.path if ctx.file.readme_md else "") + args.add(_flatten_paths(esm2015), join_with=",") + args.add(_flatten_paths(esm5), join_with=",") + args.add(_flatten_paths(bundles), join_with=",") + args.add([s.path for s in ctx.files.srcs], join_with=",") + args.add(ctx.file.stamp_data.path if ctx.file.stamp_data else "") + args.add(ctx.file.license_banner.path if ctx.file.license_banner else "") + + other_inputs = (metadata_files.to_list() + + [f.js for f in esm2015 + esm5 + bundles] + + [f.map for f in esm2015 + esm5 + bundles]) + if ctx.file.stamp_data: + other_inputs.append(ctx.file.stamp_data) + if ctx.file.readme_md: + other_inputs.append(ctx.file.readme_md) + if ctx.file.license_banner: + other_inputs.append(ctx.file.license_banner) + + ctx.actions.run( + progress_message = "Angular Packaging: building npm package for %s" % ctx.label.name, + mnemonic = "AngularPackage", + inputs = esm5_sources.to_list() + + ctx.files.deps + + ctx.files.srcs + + other_inputs, + outputs = [npm_package_directory], + executable = ctx.executable._packager, + arguments = [args], + ) + + return struct( + files = depset([npm_package_directory]) + ) + +NG_PACKAGE_ATTRS = dict(ROLLUP_ATTRS, **{ + "srcs": attr.label_list(allow_files = True), + "deps": attr.label_list(aspects = [ + rollup_module_mappings_aspect, + esm5_outputs_aspect, + ]), + "readme_md": attr.label(allow_single_file = FileType([".md"])), + "globals": attr.string_dict(default={}), + "secondary_entry_points": attr.string_list(), + "_packager": attr.label( + default=Label("//packages/bazel/src/ng_package:packager"), + executable=True, cfg="host"), + "_rollup": attr.label( + default=Label("@build_bazel_rules_nodejs//internal/rollup"), + executable=True, cfg="host"), + "_rollup_config_tmpl": attr.label( + default=Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"), + allow_single_file=True), + "_uglify": attr.label( + default=Label("@build_bazel_rules_nodejs//internal/rollup:uglify"), + executable=True, cfg="host"), +}) + +def ng_package_outputs(name, entry_point): + # Angular wants these named after the entry_point, + # eg. for //packages/core it looks like "packages/core/index.js", we want + # core.js produced by this rule. + # Currently we just borrow the entry point for this, if it looks like + # some/path/to/my/package/index.js + # we assume the files should be named "package.*.js" + basename = entry_point.split("/")[-2] if entry_point.find("/") >=0 else name + return { + "fesm5": "%s.js" % basename, + "fesm2015": "fesm2015/%s.js" % basename, + "umd": "%s.umd.js" % basename, + "umd_min": "%s.umd.min.js" % basename, + } + +ng_package = rule( + implementation = _ng_package_impl, + attrs = NG_PACKAGE_ATTRS, + outputs = ng_package_outputs, +) diff --git a/packages/bazel/src/ng_package/packager.ts b/packages/bazel/src/ng_package/packager.ts new file mode 100644 index 0000000000..3ca06ee1f0 --- /dev/null +++ b/packages/bazel/src/ng_package/packager.ts @@ -0,0 +1,141 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as shx from 'shelljs'; + +function filter(ext: string): (path: string) => boolean { + return f => f.endsWith(ext) && !f.endsWith(`.ngfactory${ext}`) && !f.endsWith(`.ngsummary${ext}`); +} + +function main(args: string[]): number { + shx.set('-e'); + const + [out, srcDir, binDir, readmeMd, fesms2015Arg, fesms5Arg, bundlesArg, srcsArg, stampData, + licenseFile] = args; + const fesms2015 = fesms2015Arg.split(',').filter(s => !!s); + const fesms5 = fesms5Arg.split(',').filter(s => !!s); + const bundles = bundlesArg.split(',').filter(s => !!s); + const srcs = srcsArg.split(',').filter(s => !!s); + + shx.mkdir('-p', out); + + let primaryEntryPoint: string|null = null; + const secondaryEntryPoints = new Set(); + + function replaceVersionPlaceholders(filePath: string) { + if (stampData) { + const version = shx.grep('BUILD_SCM_VERSION', stampData).split(' ')[1].trim(); + return shx.sed(/0.0.0-PLACEHOLDER/, version, filePath); + } + return shx.cat(filePath); + } + + function writeFesm(file: string, baseDir: string) { + const parts = path.basename(file).split('__'); + const entryPointName = parts.join('/').replace(/\..*/, ''); + if (primaryEntryPoint === null || primaryEntryPoint === entryPointName) { + primaryEntryPoint = entryPointName; + } else { + secondaryEntryPoints.add(entryPointName); + } + const filename = parts.splice(-1)[0]; + const dir = path.join(baseDir, ...parts); + shx.mkdir('-p', dir); + shx.cp(file, dir); + shx.mv(path.join(dir, path.basename(file)), path.join(dir, filename)); + } + + function moveBundleIndex(f: string) { + let ext: string; + + if (f.endsWith('.d.ts')) + ext = '.d.ts'; + else if (f.endsWith('.metadata.json')) + ext = '.metadata.json'; + else + throw new Error('Bundle index files should be .d.ts or .metadata.json'); + + const relative = path.relative(binDir, f); + let outputPath: string|undefined = undefined; + for (const secondary of secondaryEntryPoints.values()) { + if (relative.startsWith(secondary)) { + const filename = secondary.split('/').pop(); + outputPath = path.join(out, secondary, filename + ext); + } + } + if (!outputPath) { + outputPath = path.join(out, primaryEntryPoint + ext); + } + return outputPath; + } + + if (readmeMd) { + shx.cp(readmeMd, path.join(out, 'README.md')); + } + + fesms2015.forEach(fesm2015 => writeFesm(fesm2015, path.join(out, 'esm2015'))); + fesms5.forEach(fesm5 => writeFesm(fesm5, path.join(out, 'esm5'))); + + const bundlesDir = path.join(out, 'bundles'); + shx.mkdir('-p', bundlesDir); + bundles.forEach(bundle => { shx.cp(bundle, bundlesDir); }); + + const allsrcs = shx.find('-R', binDir); + allsrcs.filter(filter('.d.ts')).forEach((f: string) => { + const content = fs.readFileSync(f, {encoding: 'utf-8'}) + // Strip the named AMD module for compatibility with non-bazel users + .replace(/^\/\/\/ \n/, ''); + let outputPath: string; + if (f.endsWith('.bundle_index.d.ts')) { + outputPath = moveBundleIndex(f); + } else { + outputPath = path.join(out, path.relative(binDir, f)); + } + shx.mkdir('-p', path.dirname(outputPath)); + fs.writeFileSync(outputPath, content); + }); + + for (const src of srcs) { + replaceVersionPlaceholders(src).to(path.join(out, path.relative(srcDir, src))); + } + + allsrcs.filter(filter('.bundle_index.metadata.json')).forEach((f: string) => { + replaceVersionPlaceholders(f).to(moveBundleIndex(f)); + }); + + const licenseBanner = licenseFile ? fs.readFileSync(licenseFile, {encoding: 'utf-8'}) : ''; + + for (const secondaryEntryPoint of secondaryEntryPoints.values()) { + const baseName = secondaryEntryPoint.split('/').pop(); + const dirName = path.join(...secondaryEntryPoint.split('/').slice(0, -1)); + + fs.writeFileSync(path.join(out, dirName, `${baseName}.metadata.json`), JSON.stringify({ + '__symbolic': 'module', + 'version': 3, + 'metadata': {}, + 'exports': [{'from': `./${baseName}/${baseName}`}], + 'flatModuleIndexRedirect': true + }) + '\n'); + + fs.writeFileSync( + path.join(out, dirName, `${baseName}.d.ts`), + // Format carefully to match existing build.sh output + licenseBanner + ' ' + + ` + export * from './${baseName}/${baseName}' +`); + } + + return 0; +} + +if (require.main === module) { + process.exitCode = main(process.argv.slice(2)); +} diff --git a/packages/bazel/src/ng_package/tsconfig.json b/packages/bazel/src/ng_package/tsconfig.json new file mode 100644 index 0000000000..8abeba288c --- /dev/null +++ b/packages/bazel/src/ng_package/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "lib": ["es2015"] + } +} diff --git a/packages/bazel/test/ng_package/BUILD.bazel b/packages/bazel/test/ng_package/BUILD.bazel new file mode 100644 index 0000000000..2e6aa5224a --- /dev/null +++ b/packages/bazel/test/ng_package/BUILD.bazel @@ -0,0 +1,36 @@ +load("//tools:defaults.bzl", "ts_library") +load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test") + +exports_files(["package.json"]) + +# The tests in this package must run in separate targets, since they change +# working directory and therefore have mutable global state that causes test +# isolation failures. + +ts_library( + name = "core_spec_lib", + testonly = True, + srcs = ["core_package.spec.ts"], + deps = ["//packages:types"], +) + +jasmine_node_test( + name = "core_package", + srcs = [":core_spec_lib"], + data = [ + "//packages/core:npm_package", + ], +) + +ts_library( + name = "common_spec_lib", + testonly = True, + srcs = ["common_package.spec.ts"], + deps = ["//packages:types"], +) + +jasmine_node_test( + name = "common_package", + srcs = [":common_spec_lib"], + data = ["//packages/common:npm_package"], +) diff --git a/packages/bazel/test/ng_package/common_package.spec.ts b/packages/bazel/test/ng_package/common_package.spec.ts new file mode 100644 index 0000000000..5ae0a2e312 --- /dev/null +++ b/packages/bazel/test/ng_package/common_package.spec.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as path from 'path'; +import * as shx from 'shelljs'; + +shx.cd(path.join(process.env['TEST_SRCDIR'], 'angular', 'packages', 'common', 'npm_package')); + +describe('ng_package', () => { + it('should have right bundle files', () => { + expect(shx.ls('-R', 'bundles').stdout.split('\n').filter(n => !!n).sort()).toEqual([ + 'common-http-testing.umd.js', + 'common-http-testing.umd.js.map', + 'common-http-testing.umd.min.js', + 'common-http-testing.umd.min.js.map', + 'common-http.umd.js', + 'common-http.umd.js.map', + 'common-http.umd.min.js', + 'common-http.umd.min.js.map', + 'common-testing.umd.js', + 'common-testing.umd.js.map', + 'common-testing.umd.min.js', + 'common-testing.umd.min.js.map', + 'common.umd.js', + 'common.umd.js.map', + 'common.umd.min.js', + 'common.umd.min.js.map', + ]); + }); + it('should have right fesm files', () => { + const expected = [ + 'common.js', + 'common.js.map', + 'http', + 'http.js', + 'http.js.map', + 'http/testing.js', + 'http/testing.js.map', + 'testing.js', + 'testing.js.map', + ]; + expect(shx.ls('-R', 'esm5').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected); + expect(shx.ls('-R', 'esm2015').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected); + }); +}); diff --git a/packages/bazel/test/ng_package/core_package.spec.ts b/packages/bazel/test/ng_package/core_package.spec.ts new file mode 100644 index 0000000000..67483982b6 --- /dev/null +++ b/packages/bazel/test/ng_package/core_package.spec.ts @@ -0,0 +1,227 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as path from 'path'; +import * as shx from 'shelljs'; + +const corePackagePath = + path.join(process.env['TEST_SRCDIR'], 'angular', 'packages', 'core', 'npm_package'); +shx.cd(corePackagePath); + +/** + * Utility functions that allows me to create fs paths + * p`${foo}/some/${{bar}}/path` rather than path.join(foo, 'some', + */ +function p(templateStringArray: TemplateStringsArray) { + const segments = []; + for (const entry of templateStringArray) { + segments.push(...entry.split('/').filter(s => s !== '')); + } + return path.join(...segments); +} + + +describe('ng_package', () => { + + describe('misc root files', () => { + + describe('README.md', () => { + + it('should have a README.md file with basic info', () => { + expect(shx.cat('README.md')).toContain(`Angular`); + expect(shx.cat('README.md')).toContain(`https://github.com/angular/angular`); + }); + }); + }); + + + describe('primary entry-point', () => { + + describe('package.json', () => { + + const packageJson = 'package.json'; + + it('should have a package.json file', + () => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core`); }); + + + it('should contain correct version number with the PLACEHOLDER string replaced', () => { + expect(shx.grep('"version":', packageJson)).toMatch(/\d+\.\d+\.\d+(?!-PLACEHOLDER)/); + }); + + it('should contain module resolution mappings', () => { + const packageJson = 'package.json'; + expect(shx.grep('"main":', packageJson)).toContain(`./bundles/core.umd.js`); + expect(shx.grep('"module":', packageJson)).toContain(`./esm5/core.js`); + expect(shx.grep('"es2015":', packageJson)).toContain(`./esm2015/core.js`); + expect(shx.grep('"typings":', packageJson)).toContain(`./core.d.ts`); + }); + }); + + + describe('typescript support', () => { + + it('should have an index.d.ts file', + () => { expect(shx.cat('core.d.ts')).toContain(`export *`); }); + it('should not have amd module names', + () => { expect(shx.cat('public_api.d.ts')).not.toContain(' { + it('should contain externs', () => { + expect(shx.cat('src/testability/testability.externs.js')).toContain('/** @externs */'); + }); + }); + + + describe('angular metadata', () => { + + it('should have metadata.json files', + () => { expect(shx.cat('core.metadata.json')).toContain(`"__symbolic":"module"`); }); + }); + + + describe('fesm15', () => { + + it('should have a fesm15 file in the /esm2015 directory', + () => { expect(shx.cat('esm2015/core.js')).toContain(`export {`); }); + + it('should have a source map', () => { + expect(shx.cat('esm2015/core.js.map')) + .toContain(`{"version":3,"file":"core.js","sources":`); + }); + + it('should have the version info in the header', () => { + expect(shx.cat('esm2015/core.js')) + .toMatch(/@license Angular v\d+\.\d+\.\d+(?!-PLACEHOLDER)/); + }); + }); + + + describe('fesm5', () => { + + it('should have a fesm5 file in the /esm5 directory', + () => { expect(shx.cat('esm5/core.js')).toContain(`export {`); }); + + it('should have a source map', () => { + expect(shx.cat('esm5/core.js.map')).toContain(`{"version":3,"file":"core.js","sources":`); + }); + + it('should not be processed by tsickle', () => { + expect(shx.cat('esm5/core.js')).not.toContain('@fileoverview added by tsickle'); + }); + }); + + + describe('umd', () => { + + it('should have a umd file in the /bundles directory', + () => { expect(shx.ls('bundles/core.umd.js').length).toBe(1, 'File not found'); }); + + it('should have a source map next to the umd file', + () => { expect(shx.ls('bundles/core.umd.js.map').length).toBe(1, 'File not found'); }); + + it('should have a minified umd file in the /bundles directory', + () => { expect(shx.ls('bundles/core.umd.min.js').length).toBe(1, 'File not found'); }); + + it('should have a source map next to the minified umd file', + () => { expect(shx.ls('bundles/core.umd.min.js.map').length).toBe(1, 'File not found'); }); + }); + }); + + describe('secondary entry-point', () => { + describe('package.json', () => { + + const packageJson = p `testing/package.json`; + + it('should have a package.json file', + () => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core/testing`); }); + + it('should have its module resolution mappings defined in the nested package.json', () => { + const packageJson = p `testing/package.json`; + expect(shx.grep('"main":', packageJson)).toContain(`../bundles/core-testing.umd.js`); + expect(shx.grep('"module":', packageJson)).toContain(`../esm5/testing.js`); + expect(shx.grep('"es2015":', packageJson)).toContain(`../esm2015/testing.js`); + expect(shx.grep('"typings":', packageJson)).toContain(`./testing.d.ts`); + }); + }); + + describe('typings', () => { + const typingsFile = p `testing/testing.d.ts`; + it('should have a typings file', + () => { expect(shx.cat(typingsFile)).toContain('export * from \'./public_api\';'); }); + }); + describe('typescript support', () => { + + // TODO(i): why in the parent dir? + it('should have an \'redirect\' d.ts file in the parent dir', + () => { expect(shx.cat('testing.d.ts')).toContain(`export *`); }); + + it('should have a \'actual\' d.ts file in the parent dir', () => { + expect(shx.cat('testing/testing.d.ts')).toContain(`export * from './public_api';`); + }); + }); + + describe('angular metadata file', () => { + it('should have a \'redirect\' metadata.json file next to the d.ts file', () => { + expect(shx.cat('testing.metadata.json')) + .toContain(`"exports":[{"from":"./testing/testing"}],"flatModuleIndexRedirect":true`); + }); + + it('should have an \'actual\' metadata.json file', () => { + expect(shx.cat('testing/testing.metadata.json')) + .toContain(`"metadata":{"async":{"__symbolic":"function"},`); + }); + }); + + describe('fesm15', () => { + + it('should have a fesm15 file in the /esm2015 directory', + () => { expect(shx.cat('esm2015/testing.js')).toContain(`export {`); }); + + it('should have a source map', () => { + expect(shx.cat('esm2015/testing.js.map')) + .toContain(`{"version":3,"file":"testing.js","sources":`); + }); + + it('should have the version info in the header', () => { + expect(shx.cat('esm2015/testing.js')) + .toMatch(/@license Angular v\d+\.\d+\.\d+(?!-PLACEHOLDER)/); + }); + }); + + describe('fesm5', () => { + it('should have a fesm5 file in the /esm5 directory', + () => { expect(shx.cat('esm5/testing.js')).toContain(`export {`); }); + + it('should have a source map', () => { + expect(shx.cat('esm5/testing.js.map')) + .toContain(`{"version":3,"file":"testing.js","sources":`); + }); + }); + + describe('umd', () => { + + it('should have a umd file in the /bundles directory', + () => { expect(shx.ls('bundles/core-testing.umd.js').length).toBe(1, 'File not found'); }); + + it('should have a source map next to the umd file', () => { + expect(shx.ls('bundles/core-testing.umd.js.map').length).toBe(1, 'File not found'); + }); + + it('should have a minified umd file in the /bundles directory', () => { + expect(shx.ls('bundles/core-testing.umd.min.js').length).toBe(1, 'File not found'); + }); + + it('should have a source map next to the minified umd file', () => { + expect(shx.ls('bundles/core-testing.umd.min.js.map').length).toBe(1, 'File not found'); + }); + }); + }); +}); diff --git a/packages/common/BUILD.bazel b/packages/common/BUILD.bazel index 1eb16434eb..4766fa872f 100644 --- a/packages/common/BUILD.bazel +++ b/packages/common/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ng_module") +load("//tools:defaults.bzl", "ng_module", "ng_package") ng_module( name = "common", @@ -16,3 +16,19 @@ ng_module( "@rxjs", ], ) + +ng_package( + name = "npm_package", + entry_point = "packages/common/index.js", + secondary_entry_points = [ + "testing", + "http", + "http/testing", + ], + deps = [ + "//packages/common", + "//packages/common/http", + "//packages/common/http/testing", + "//packages/common/testing", + ], +) diff --git a/packages/compiler-cli/integrationtest/bazel/ng_module/BUILD.bazel b/packages/compiler-cli/integrationtest/bazel/ng_module/BUILD.bazel index bc070a3baa..84142c0999 100644 --- a/packages/compiler-cli/integrationtest/bazel/ng_module/BUILD.bazel +++ b/packages/compiler-cli/integrationtest/bazel/ng_module/BUILD.bazel @@ -3,6 +3,7 @@ load("//packages/bazel:index.bzl", "ng_module") ng_module( name = "test_module", srcs = glob(["*.ts"]), + entry_point = "index.ts", module_name = "some_npm_module", deps = ["//packages/core"], ) diff --git a/packages/core/BUILD.bazel b/packages/core/BUILD.bazel index 0ac5dc22f7..2f0f1d111f 100644 --- a/packages/core/BUILD.bazel +++ b/packages/core/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ng_module") +load("//tools:defaults.bzl", "ng_module", "ng_package") ng_module( name = "core", @@ -16,3 +16,14 @@ ng_module( "@rxjs", ], ) + +ng_package( + name = "npm_package", + srcs = glob(["**/*.externs.js"] + ["**/package.json"]) + ["//packages/core/testing:npm_package_assets"], + entry_point = "packages/core/index.js", + secondary_entry_points = ["testing"], + deps = [ + ":core", + "//packages/core/testing", + ], +) diff --git a/packages/core/testing/BUILD.bazel b/packages/core/testing/BUILD.bazel index 3d9ac4daaa..458520a858 100644 --- a/packages/core/testing/BUILD.bazel +++ b/packages/core/testing/BUILD.bazel @@ -4,10 +4,17 @@ load("//tools:defaults.bzl", "ng_module") ng_module( name = "testing", - srcs = glob(["**/*.ts"]), + srcs = glob( + ["**/*.ts"], + ), module_name = "@angular/core/testing", deps = [ "//packages:types", "//packages/core", ], ) + +filegroup( + name = "npm_package_assets", + srcs = ["package.json"], +) diff --git a/tools/defaults.bzl b/tools/defaults.bzl index b49c73a6f6..9a4d24b870 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -1,6 +1,6 @@ """Re-export of some bazel rules with repository-wide defaults.""" load("@build_bazel_rules_typescript//:defs.bzl", _ts_library = "ts_library") -load("//packages/bazel:index.bzl", _ng_module = "ng_module") +load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") DEFAULT_TSCONFIG = "//packages:tsconfig-build.json" @@ -9,7 +9,24 @@ def ts_library(tsconfig = None, **kwargs): tsconfig = DEFAULT_TSCONFIG _ts_library(tsconfig = tsconfig, **kwargs) -def ng_module(name, tsconfig = None, **kwargs): +def ng_module(name, tsconfig = None, entry_point = None, **kwargs): if not tsconfig: tsconfig = DEFAULT_TSCONFIG - _ng_module(name = name, tsconfig = tsconfig, **kwargs) + if not entry_point: + entry_point = "public_api.ts" + _ng_module(name = name, tsconfig = tsconfig, entry_point = entry_point, **kwargs) + +def ng_package(name, readme_md = None, license_banner = None, stamp_data = None, **kwargs): + if not readme_md: + readme_md = "//packages:README.md" + if not license_banner: + license_banner = "//packages:license-banner.txt" + if not stamp_data: + stamp_data = "//tools:stamp_data" + + _ng_package( + name = name, + readme_md = readme_md, + license_banner = license_banner, + stamp_data = stamp_data, + **kwargs) diff --git a/yarn.lock b/yarn.lock index 05f87a79e0..65feb7dacd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -91,12 +91,24 @@ dependencies: "@types/node" "*" +"@types/events@*": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-1.1.0.tgz#93b1be91f63c184450385272c47b6496fd028e02" + "@types/fs-extra@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.2.tgz#7b9b1bbf85962cbe029b5a83c9b530d7c75af3ba" dependencies: "@types/node" "*" +"@types/glob@*": + version "5.0.35" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + "@types/hammerjs@2.0.35": version "2.0.35" resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.35.tgz#7b7c950c7d54593e23bffc8d2b4feba9866a7277" @@ -105,6 +117,10 @@ version "2.2.22-alpha" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.2.22-alpha.tgz#eecaee43fe42ef6b5cfefad1bcc370c433f485bf" +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + "@types/node@*": version "8.0.28" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60" @@ -125,6 +141,13 @@ version "2.53.42" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac" +"@types/shelljs@^0.7.8": + version "0.7.8" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.8.tgz#4b4d6ee7926e58d7bca448a50ba442fd9f6715bd" + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/source-map@^0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@types/source-map/-/source-map-0.5.1.tgz#7e74db5d06ab373a712356eebfaea2fad0ea2367"