From 523c785e8ff4c819f10267e7d1e4fec113ea7985 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 2 Feb 2020 16:03:34 +0200 Subject: [PATCH] fix(ngcc): correctly invalidate cache when moving/removing files/directories (#35106) One particular scenario where this was causing problems was when the [BackupFileCleaner][1] restored a file (such as a `.d.ts` file) by [moving the backup file][2] to its original location, but the modified content was kept in the cache. [1]: https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L54 [2]: https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L61 Fixes #35095 PR Close #35106 --- integration/ngcc/mock-ngcc-version-marker.js | 50 ++++++ integration/ngcc/test.sh | 44 ++++- integration/ngcc/yarn.lock | 152 +++++++++++++----- .../file_system/src/cached_file_system.ts | 18 ++- .../test/cached_file_system_spec.ts | 49 +++++- 5 files changed, 267 insertions(+), 46 deletions(-) create mode 100644 integration/ngcc/mock-ngcc-version-marker.js diff --git a/integration/ngcc/mock-ngcc-version-marker.js b/integration/ngcc/mock-ngcc-version-marker.js new file mode 100644 index 0000000000..e72c998e5c --- /dev/null +++ b/integration/ngcc/mock-ngcc-version-marker.js @@ -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 + */ + +/** + * **Usage:** + * ```sh + * node mock-ngcc-version-marker + * + * # Example: + * # node mock-ngcc-version-marker @angular/material/button 3.0.0 + * ``` + * + * Ngcc marks entry-points as processed by adding marker properties in their `package.json`. It uses + * a version as a marker in order to be able to distinguish entry-points processed by a different + * ngcc version (e.g. an older version after an update) and perform the necessary clean-up. + * + * This script replaces the ngcc version marker in an entry-point's `package.json` to make it appear + * as if it were processed by a different version of ngcc. This allows testing the clean-up logic + * without the overhead of actually installing a different ngcc version, compiling the dependencies, + * then installing the current version and compiling again. + */ + +const {writeFileSync} = require('fs'); +const {basename} = require('path'); + +const entryPointName = process.argv[2]; +const mockNgccVersion = process.argv[3]; +const actualNgccVersion = require('@angular/compiler-cli/package.json').version; + +if (!entryPointName || !mockNgccVersion) { + throw new Error( + 'Missing required argument(s).\n' + + `Usage: node ${basename(__filename)} \n`); +} + +const entryPointPkgJsonPath = require.resolve(`${entryPointName}/package.json`); +const entryPointPkgJson = require(entryPointPkgJsonPath); +const processedMarkers = entryPointPkgJson.__processed_by_ivy_ngcc__; + +Object.keys(processedMarkers).forEach(key => processedMarkers[key] = mockNgccVersion); +writeFileSync(entryPointPkgJsonPath, JSON.stringify(entryPointPkgJson, null, 2)); + +console.log( + `Successfully mocked ngcc version marker in '${entryPointName}': ` + + `${actualNgccVersion} --> ${mockNgccVersion}`); diff --git a/integration/ngcc/test.sh b/integration/ngcc/test.sh index 32adc70f10..70ce608866 100755 --- a/integration/ngcc/test.sh +++ b/integration/ngcc/test.sh @@ -3,7 +3,7 @@ # Do not immediately exit on error to allow the `assertSucceeded` function to handle the error. # # NOTE: -# Each statement should be followed by an `assertSucceeded`/`assertFailed` or `exit 1` statement. +# Each statement should be followed by an `assert*` or `exit 1` statement. set +e -x PATH=$PATH:$(npm bin) @@ -22,6 +22,26 @@ function assertSucceeded { fi } +function assertEquals { + local value1=$1; + local value2=$2; + + if [[ "$value1" != "$value2" ]]; then + echo "FAIL: Expected '$value1' to equal '$value2'." + exit 1; + fi +} + +function assertNotEquals { + local value1=$1; + local value2=$2; + + if [[ "$value1" == "$value2" ]]; then + echo "FAIL: Expected '$value1' not to equal '$value2'." + exit 1; + fi +} + ngcc --help assertSucceeded "Expected 'ngcc --help' to succeed." @@ -140,6 +160,27 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'." assertSucceeded "Expected 'ngcc' to not add trailing commas to factory function parameters in UMD." +# Can it correctly clean up and re-compile when dependencies are already compiled by a different version? + readonly actualNgccVersion=`node --print "require('@angular/compiler-cli/package.json').version"` + readonly mockNgccVersion="3.0.0" + + # Mock the ngcc version marker on a package to make it appear as if it is compiled by a different ngcc version. + node mock-ngcc-version-marker @angular/material/button $mockNgccVersion + assertSucceeded "Expected to successfully mock the 'ngcc' version marker in '@angular/material/button'." + assertEquals $mockNgccVersion `node --print "require('@angular/material/button/package.json').__processed_by_ivy_ngcc__.main"` + assertEquals 1 `cat node_modules/@angular/material/button/button.d.ts | grep 'import \* as ɵngcc0' | wc -l` + + # Re-compile packages (which requires cleaning up those compiled by a different ngcc version). + # (Use sync mode to ensure all tasks share the same `CachedFileSystem` instance.) + ngcc --no-async --properties main + assertSucceeded "Expected 'ngcc' to successfully re-compile the packages." + + # Ensure previously compiled packages were correctly cleaned up (i.e. no multiple + # `import ... ɵngcc0` statements) and re-compiled by the current ngcc version. + assertEquals $actualNgccVersion `node --print "require('@angular/material/button/package.json').__processed_by_ivy_ngcc__.main"` + assertEquals 1 `cat node_modules/@angular/material/button/button.d.ts | grep 'import \* as ɵngcc0' | wc -l` + + # Can it compile `@angular/platform-server` in UMD + typings without errors? # (The CLI prefers the `main` property (which maps to UMD) over `module` when compiling `@angular/platform-server`. # See https://github.com/angular/angular-cli/blob/e36853338/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/server.ts#L34) @@ -151,6 +192,7 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'." ngcc --properties main --target @angular/platform-server assertSucceeded "Expected 'ngcc' to successfully compile '@angular/platform-server' (main)." + # Can it be safely run again (as a noop)? # And check that it logged skipping compilation as expected ngcc -l debug | grep 'Skipping' diff --git a/integration/ngcc/yarn.lock b/integration/ngcc/yarn.lock index 197e362d42..2e1b49e367 100644 --- a/integration/ngcc/yarn.lock +++ b/integration/ngcc/yarn.lock @@ -19,9 +19,10 @@ version "9.0.0-rc.1" dependencies: canonical-path "1.0.0" - chokidar "^2.1.1" + chokidar "^3.0.0" convert-source-map "^1.5.1" dependency-graph "^0.7.2" + fs-extra "4.0.2" magic-string "^0.25.0" minimist "^1.2.0" reflect-metadata "^0.1.2" @@ -154,6 +155,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -248,11 +257,6 @@ async-each@^1.0.0: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" integrity sha1-GdOGodntxufByF04iu28xW0zYC0= -async-each@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.2.tgz#8b8a7ca2a658f927e9f307d6d1a42f4199f0f735" - integrity sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg== - async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" @@ -348,6 +352,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" @@ -377,7 +386,7 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: +braces@^2.3.0, braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -393,6 +402,13 @@ braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-sync-client@^2.26.2: version "2.26.2" resolved "https://registry.yarnpkg.com/browser-sync-client/-/browser-sync-client-2.26.2.tgz#dd0070c80bdc6d9021e89f7837ee70ed0a8acf91" @@ -560,24 +576,20 @@ chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.2" -chokidar@^2.1.1: - version "2.1.5" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d" - integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A== +chokidar@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.3.0" optionalDependencies: - fsevents "^1.2.7" + fsevents "~2.1.2" chownr@^1.0.1: version "1.1.1" @@ -1119,6 +1131,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" @@ -1201,6 +1220,15 @@ fs-extra@3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" + integrity sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -1221,13 +1249,10 @@ fsevents@^1.2.2: nan "^2.9.2" node-pre-gyp "^0.10.0" -fsevents@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" - integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== - dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" +fsevents@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== gauge@~2.7.3: version "2.7.4" @@ -1295,6 +1320,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + glob@^7.0.3, glob@^7.0.5, glob@^7.0.6: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -1488,7 +1520,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -1534,6 +1566,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -1645,6 +1684,13 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + is-number-like@^1.0.3: version "1.0.8" resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" @@ -1671,6 +1717,11 @@ is-number@^4.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -1810,6 +1861,13 @@ jsonfile@^3.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -2189,7 +2247,7 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -2479,6 +2537,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.4, picomatch@^2.0.7: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -2520,7 +2583,7 @@ process-nextick-args@~2.0.0: integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== "protractor@file:../../node_modules/protractor": - version "5.4.2" + version "5.4.3" dependencies: "@types/q" "^0.0.32" "@types/selenium-webdriver" "^3.0.0" @@ -2640,7 +2703,7 @@ readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdirp@^2.0.0, readdirp@^2.2.1: +readdirp@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== @@ -2649,6 +2712,13 @@ readdirp@^2.0.0, readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + dependencies: + picomatch "^2.0.7" + reflect-metadata@^0.1.2: version "0.1.12" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" @@ -3276,6 +3346,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -3317,7 +3394,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= "typescript@file:../../node_modules/typescript": - version "3.6.4" + version "3.7.4" ua-parser-js@0.7.17: version "0.7.17" @@ -3362,11 +3439,6 @@ upath@^1.0.5: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== -upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== - urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts index e37eea75ec..92030c58c7 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts @@ -72,12 +72,16 @@ export class CachedFileSystem implements FileSystem { moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { this.delegate.moveFile(from, to); + this.existsCache.set(from, false); + this.existsCache.set(to, true); + if (this.readFileCache.has(from)) { this.readFileCache.set(to, this.readFileCache.get(from)); this.readFileCache.delete(from); + } else { + this.readFileCache.delete(to); } - this.existsCache.set(to, true); } ensureDir(path: AbsoluteFsPath): void { @@ -90,10 +94,18 @@ export class CachedFileSystem implements FileSystem { removeDeep(path: AbsoluteFsPath): void { this.delegate.removeDeep(path); - // Clear out all children of this directory from the exists cache. + + // Clear out this directory and all its children from the `exists` cache. for (const p of this.existsCache.keys()) { if (p.startsWith(path)) { - this.existsCache.set(path, false); + this.existsCache.set(p, false); + } + } + + // Clear out this directory and all its children from the `readFile` cache. + for (const p of this.readFileCache.keys()) { + if (p.startsWith(path)) { + this.readFileCache.delete(p); } } } diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts index 02dfdf01d3..04610ddaa1 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {CachedFileSystem} from '../src/cached_file_system'; -import {absoluteFrom, setFileSystem} from '../src/helpers'; +import {absoluteFrom, join, setFileSystem} from '../src/helpers'; import {NodeJSFileSystem} from '../src/node_js_file_system'; import {AbsoluteFsPath, FileSystem} from '../src/types'; @@ -236,7 +236,7 @@ describe('CachedFileSystem', () => { expect(readFileSpy).toHaveBeenCalledWith(abcPath); }); - it('should update the `to` "readFile" cache', () => { + it('should update the `to` "readFile" cache (if `from` was cached)', () => { spyOn(delegate, 'moveFile'); const readFileSpy = spyOn(delegate, 'readFile'); @@ -252,6 +252,24 @@ describe('CachedFileSystem', () => { expect(fs.readFile(xyzPath)).toEqual('abc content'); expect(readFileSpy).not.toHaveBeenCalled(); }); + + it('should delete the `to` "readFile" cache (if `from` was not cached)', () => { + spyOn(delegate, 'moveFile'); + const readFileSpy = spyOn(delegate, 'readFile'); + + // Fill the xyz "readFile" cache + readFileSpy.and.returnValue('xyz content'); + fs.readFile(xyzPath); + readFileSpy.calls.reset(); + + // Move the file + fs.moveFile(abcPath, xyzPath); + + // Show that the cache was not hit for the xyz file + readFileSpy.and.returnValue('abc content'); + expect(fs.readFile(xyzPath)).toBe('abc content'); + expect(readFileSpy).toHaveBeenCalledWith(xyzPath); + }); }); describe('ensureDir()', () => { @@ -281,14 +299,41 @@ describe('CachedFileSystem', () => { }); it('should update the exists cache', () => { + spyOn(delegate, 'writeFile'); spyOn(delegate, 'removeDeep'); const existsSpy = spyOn(delegate, 'exists').and.returnValue(true); expect(fs.exists(abcPath)).toBe(true); existsSpy.calls.reset(); + // Create a file inside `/a/b/c`. + const abcdPath = join(abcPath, 'd'); + fs.writeFile(abcdPath, 'content'); + expect(fs.exists(abcdPath)).toBe(true); + expect(existsSpy).not.toHaveBeenCalled(); + + // Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content). fs.removeDeep(abcPath); expect(fs.exists(abcPath)).toBeFalsy(); + expect(fs.exists(abcdPath)).toBeFalsy(); expect(existsSpy).not.toHaveBeenCalled(); }); + + it('should update the readFile cache', () => { + spyOn(delegate, 'writeFile'); + spyOn(delegate, 'removeDeep'); + spyOn(delegate, 'lstat').and.throwError('ENOENT: no such file or directory'); + const readFileSpy = spyOn(delegate, 'readFile'); + + // Create a file inside `/a/b/c`. + const abcdPath = join(abcPath, 'd'); + fs.writeFile(abcdPath, 'content from cache'); + expect(fs.readFile(abcdPath)).toBe('content from cache'); + expect(readFileSpy).not.toHaveBeenCalled(); + + // Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content). + fs.removeDeep(abcPath); + expect(() => fs.readFile(abcdPath)).toThrowError('ENOENT: no such file or directory'); + expect(() => fs.readFile(abcPath)).toThrowError('ENOENT: no such file or directory'); + }); }); });