diff --git a/.eslintrc.js b/.eslintrc.js index 2def24dc8dcb3..4f085721abe28 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -84,5 +84,6 @@ module.exports = { spyOnDevAndProd: true, spyOnProd: true, __PROFILE__: true, + __UMD__: true, }, }; diff --git a/fixtures/packaging/browserify/dev/package.json b/fixtures/packaging/browserify/dev/package.json index 059ce202ba8b3..2a876345fb27b 100644 --- a/fixtures/packaging/browserify/dev/package.json +++ b/fixtures/packaging/browserify/dev/package.json @@ -4,7 +4,8 @@ "dependencies": { "browserify": "^13.3.0", "react": "link:../../../../build/node_modules/react", - "react-dom": "link:../../../../build/node_modules/react-dom" + "react-dom": "link:../../../../build/node_modules/react-dom", + "react-scheduler": "link:../../../../build/node_modules/react-scheduler" }, "scripts": { "build": "rm -f output.js && browserify ./input.js -o output.js" diff --git a/fixtures/packaging/browserify/dev/yarn.lock b/fixtures/packaging/browserify/dev/yarn.lock index b1a8fa7a87ac8..80e5c53b50981 100644 --- a/fixtures/packaging/browserify/dev/yarn.lock +++ b/fixtures/packaging/browserify/dev/yarn.lock @@ -25,10 +25,6 @@ array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" @@ -254,10 +250,6 @@ convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -365,12 +357,6 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -382,18 +368,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -455,10 +429,6 @@ https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@~0.4.13: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -505,10 +475,6 @@ is-buffer@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -517,16 +483,9 @@ isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - -js-tokens@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" json-stable-stringify@~0.0.0: version "0.0.1" @@ -560,11 +519,11 @@ lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" +loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: - js-tokens "^3.0.0" + js-tokens "^3.0.0 || ^4.0.0" md5.js@^1.3.4: version "1.3.4" @@ -618,14 +577,7 @@ module-deps@^4.0.8: through2 "^2.0.0" xtend "^4.0.0" -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -693,17 +645,10 @@ process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: - asap "~2.0.3" - -prop-types@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" - dependencies: - fbjs "^0.8.16" loose-envify "^1.3.1" object-assign "^4.1.1" @@ -743,6 +688,14 @@ randombytes@^2.0.0, randombytes@^2.0.1: version "0.0.0" uid "" +react-scheduler@^0.1.0-alpha-1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/react-scheduler/-/react-scheduler-0.1.0.tgz#d16c0ee90d2895ff1941e0d681293dbe4f4b4f38" + +"react-scheduler@link:../../../../build/node_modules/react-scheduler": + version "0.0.0" + uid "" + "react@link:../../../../build/node_modules/react": version "0.0.0" uid "" @@ -797,10 +750,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.9" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" @@ -910,10 +859,6 @@ typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" - umd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" @@ -941,10 +886,6 @@ vm-browserify@~0.0.1: dependencies: indexof "0.0.1" -whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/fixtures/packaging/browserify/prod/package.json b/fixtures/packaging/browserify/prod/package.json index d966e899e405f..7714a5f1a7d96 100644 --- a/fixtures/packaging/browserify/prod/package.json +++ b/fixtures/packaging/browserify/prod/package.json @@ -4,7 +4,8 @@ "dependencies": { "browserify": "^13.3.0", "react": "link:../../../../build/node_modules/react", - "react-dom": "link:../../../../build/node_modules/react-dom" + "react-dom": "link:../../../../build/node_modules/react-dom", + "react-scheduler": "link:../../../../build/node_modules/react-scheduler" }, "scripts": { "build": "rm -f output.js && browserify ./input.js -g [envify --NODE_ENV 'production'] -o output.js" diff --git a/fixtures/packaging/browserify/prod/yarn.lock b/fixtures/packaging/browserify/prod/yarn.lock index 64b3dd9df4289..73a8afa9b8490 100644 --- a/fixtures/packaging/browserify/prod/yarn.lock +++ b/fixtures/packaging/browserify/prod/yarn.lock @@ -25,10 +25,6 @@ array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" @@ -254,10 +250,6 @@ convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -365,12 +357,6 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - envify@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" @@ -393,18 +379,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -466,10 +440,6 @@ https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@~0.4.13: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -516,10 +486,6 @@ is-buffer@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -528,16 +494,9 @@ isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - -js-tokens@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" json-stable-stringify@~0.0.0: version "0.0.1" @@ -571,11 +530,11 @@ lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" +loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: - js-tokens "^3.0.0" + js-tokens "^3.0.0 || ^4.0.0" md5.js@^1.3.4: version "1.3.4" @@ -629,14 +588,7 @@ module-deps@^4.0.8: through2 "^2.0.0" xtend "^4.0.0" -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -704,17 +656,10 @@ process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: - asap "~2.0.3" - -prop-types@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" - dependencies: - fbjs "^0.8.16" loose-envify "^1.3.1" object-assign "^4.1.1" @@ -754,6 +699,14 @@ randombytes@^2.0.0, randombytes@^2.0.1: version "0.0.0" uid "" +react-scheduler@^0.1.0-alpha-1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/react-scheduler/-/react-scheduler-0.1.0.tgz#d16c0ee90d2895ff1941e0d681293dbe4f4b4f38" + +"react-scheduler@link:../../../../build/node_modules/react-scheduler": + version "0.0.0" + uid "" + "react@link:../../../../build/node_modules/react": version "0.0.0" uid "" @@ -808,10 +761,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.9" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" @@ -921,10 +870,6 @@ typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" - umd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" @@ -952,10 +897,6 @@ vm-browserify@~0.0.1: dependencies: indexof "0.0.1" -whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/fixtures/packaging/brunch/dev/package.json b/fixtures/packaging/brunch/dev/package.json index 69a572eeb3bb6..8b9d7c4dcfbec 100644 --- a/fixtures/packaging/brunch/dev/package.json +++ b/fixtures/packaging/brunch/dev/package.json @@ -5,7 +5,8 @@ "brunch": "^2.9.1", "javascript-brunch": "^2.0.0", "react": "link:../../../../build/node_modules/react", - "react-dom": "link:../../../../build/node_modules/react-dom" + "react-dom": "link:../../../../build/node_modules/react-dom", + "react-scheduler": "link:../../../../build/node_modules/react-scheduler" }, "scripts": { "build": "rm -rf public && brunch build" diff --git a/fixtures/packaging/brunch/dev/yarn.lock b/fixtures/packaging/brunch/dev/yarn.lock index 610a93e9f0572..fcb9cea68589d 100644 --- a/fixtures/packaging/brunch/dev/yarn.lock +++ b/fixtures/packaging/brunch/dev/yarn.lock @@ -78,10 +78,6 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" @@ -413,10 +409,6 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -604,12 +596,6 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - es-abstract@^1.6.1: version "1.9.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.9.0.tgz#690829a07cae36b222e7fd9b75c0d0573eb25227" @@ -726,18 +712,6 @@ fast-levenshtein@^1.1.3: version "1.1.4" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" -fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - fcache@~0.3: version "0.3.0" resolved "https://registry.yarnpkg.com/fcache/-/fcache-0.3.0.tgz#d45f2f908642b91b798e88195ec47881a51c3d44" @@ -1027,7 +1001,7 @@ https-browserify@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@0.4.19, iconv-lite@~0.4.13: +iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -1152,10 +1126,6 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" @@ -1182,13 +1152,6 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1264,7 +1227,7 @@ loggy@~0.3.0: ansicolors "~0.3.2" growl "~1.8.1" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.1.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -1423,13 +1386,6 @@ node-browser-modules@^0.1.0: util "~0.10.3" vm-browserify "~0.0.4" -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-pre-gyp@^0.6.36: version "0.6.38" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz#e92a20f83416415bb4086f6d1fb78b3da73d113d" @@ -1605,17 +1561,10 @@ promise.prototype.finally@^2: es-abstract "^1.6.1" function-bind "^1.1.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - dependencies: - asap "~2.0.3" - -prop-types@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: - fbjs "^0.8.16" loose-envify "^1.3.1" object-assign "^4.1.1" @@ -1708,6 +1657,14 @@ rc@^1.1.7: version "0.0.0" uid "" +react-scheduler@^0.1.0-alpha-1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/react-scheduler/-/react-scheduler-0.1.0.tgz#d16c0ee90d2895ff1941e0d681293dbe4f4b4f38" + +"react-scheduler@link:../../../../build/node_modules/react-scheduler": + version "0.0.0" + uid "" + "react@link:../../../../build/node_modules/react": version "0.0.0" uid "" @@ -1870,10 +1827,6 @@ 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" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" @@ -2053,10 +2006,6 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" - uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -2118,10 +2067,6 @@ vm-browserify@~0.0.4: dependencies: indexof "0.0.1" -whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" - which@^1.2.12: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" diff --git a/fixtures/packaging/brunch/prod/package.json b/fixtures/packaging/brunch/prod/package.json index 065461bafd8ad..fb18aa709595f 100644 --- a/fixtures/packaging/brunch/prod/package.json +++ b/fixtures/packaging/brunch/prod/package.json @@ -5,7 +5,8 @@ "brunch": "^2.9.1", "javascript-brunch": "^2.0.0", "react": "link:../../../../build/node_modules/react", - "react-dom": "link:../../../../build/node_modules/react-dom" + "react-dom": "link:../../../../build/node_modules/react-dom", + "react-scheduler": "link:../../../../build/node_modules/react-scheduler" }, "scripts": { "build": "rm -rf public && brunch build -p" diff --git a/fixtures/packaging/brunch/prod/yarn.lock b/fixtures/packaging/brunch/prod/yarn.lock index 610a93e9f0572..fcb9cea68589d 100644 --- a/fixtures/packaging/brunch/prod/yarn.lock +++ b/fixtures/packaging/brunch/prod/yarn.lock @@ -78,10 +78,6 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" @@ -413,10 +409,6 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -604,12 +596,6 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - es-abstract@^1.6.1: version "1.9.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.9.0.tgz#690829a07cae36b222e7fd9b75c0d0573eb25227" @@ -726,18 +712,6 @@ fast-levenshtein@^1.1.3: version "1.1.4" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" -fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - fcache@~0.3: version "0.3.0" resolved "https://registry.yarnpkg.com/fcache/-/fcache-0.3.0.tgz#d45f2f908642b91b798e88195ec47881a51c3d44" @@ -1027,7 +1001,7 @@ https-browserify@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@0.4.19, iconv-lite@~0.4.13: +iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -1152,10 +1126,6 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" @@ -1182,13 +1152,6 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1264,7 +1227,7 @@ loggy@~0.3.0: ansicolors "~0.3.2" growl "~1.8.1" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.1.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -1423,13 +1386,6 @@ node-browser-modules@^0.1.0: util "~0.10.3" vm-browserify "~0.0.4" -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-pre-gyp@^0.6.36: version "0.6.38" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz#e92a20f83416415bb4086f6d1fb78b3da73d113d" @@ -1605,17 +1561,10 @@ promise.prototype.finally@^2: es-abstract "^1.6.1" function-bind "^1.1.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - dependencies: - asap "~2.0.3" - -prop-types@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: - fbjs "^0.8.16" loose-envify "^1.3.1" object-assign "^4.1.1" @@ -1708,6 +1657,14 @@ rc@^1.1.7: version "0.0.0" uid "" +react-scheduler@^0.1.0-alpha-1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/react-scheduler/-/react-scheduler-0.1.0.tgz#d16c0ee90d2895ff1941e0d681293dbe4f4b4f38" + +"react-scheduler@link:../../../../build/node_modules/react-scheduler": + version "0.0.0" + uid "" + "react@link:../../../../build/node_modules/react": version "0.0.0" uid "" @@ -1870,10 +1827,6 @@ 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" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" @@ -2053,10 +2006,6 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" - uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -2118,10 +2067,6 @@ vm-browserify@~0.0.4: dependencies: indexof "0.0.1" -whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" - which@^1.2.12: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" diff --git a/fixtures/packaging/rjs/dev/config.js b/fixtures/packaging/rjs/dev/config.js index b955f32efe97c..6db973085ee4e 100644 --- a/fixtures/packaging/rjs/dev/config.js +++ b/fixtures/packaging/rjs/dev/config.js @@ -6,5 +6,6 @@ module.exports = { paths: { react: '../../../../build/dist/react.development', 'react-dom': '../../../../build/dist/react-dom.development', + 'react-scheduler': '../../../../build/dist/react-scheduler.development', }, }; diff --git a/fixtures/packaging/rjs/prod/config.js b/fixtures/packaging/rjs/prod/config.js index 86d76331a2220..d096ece492c39 100644 --- a/fixtures/packaging/rjs/prod/config.js +++ b/fixtures/packaging/rjs/prod/config.js @@ -6,5 +6,6 @@ module.exports = { paths: { react: '../../../../build/dist/react.production.min', 'react-dom': '../../../../build/dist/react-dom.production.min', + 'react-scheduler': '../../../../build/dist/react-scheduler.development', }, }; diff --git a/fixtures/packaging/systemjs-builder/dev/config.js b/fixtures/packaging/systemjs-builder/dev/config.js index 2cd1b3d66f9ce..ec580e14fb7ae 100644 --- a/fixtures/packaging/systemjs-builder/dev/config.js +++ b/fixtures/packaging/systemjs-builder/dev/config.js @@ -2,5 +2,6 @@ System.config({ paths: { react: '../../../../build/dist/react.development.js', 'react-dom': '../../../../build/dist/react-dom.development.js', + 'react-scheduler': '../../../../build/dist/react-scheduler.development', }, }); diff --git a/fixtures/packaging/systemjs-builder/prod/config.js b/fixtures/packaging/systemjs-builder/prod/config.js index bb981fe2b7e59..1dde1b64cd561 100644 --- a/fixtures/packaging/systemjs-builder/prod/config.js +++ b/fixtures/packaging/systemjs-builder/prod/config.js @@ -2,5 +2,6 @@ System.config({ paths: { react: '../../../../build/dist/react.production.min.js', 'react-dom': '../../../../build/dist/react-dom.production.min.js', + 'react-scheduler': '../../../../build/dist/react-scheduler.development', }, }); diff --git a/fixtures/schedule/index.html b/fixtures/schedule/index.html index ad5f1042f561c..34e98a0bd80ba 100644 --- a/fixtures/schedule/index.html +++ b/fixtures/schedule/index.html @@ -92,7 +92,11 @@

Tests:

+ + + + + + diff --git a/fixtures/tracking/script.js b/fixtures/tracking/script.js new file mode 100644 index 0000000000000..b146a56ebc961 --- /dev/null +++ b/fixtures/tracking/script.js @@ -0,0 +1,208 @@ +function runTest(listItem, callback) { + try { + callback(); + listItem.className = 'correct'; + listItem.setAttribute('data-value', 'All checks pass'); + } catch (error) { + listItem.className = 'incorrect'; + listItem.setAttribute('data-value', error); + } +} + +function runAllTests() { + try { + checkSchedulerAPI(); + } finally { + try { + checkSchedulerTrackingAPI(); + } finally { + try { + checkSchedulerTrackingSubscriptionsAPI(); + } finally { + checkEndToEndIntegration(); + } + } + } +} + +function checkSchedulerAPI() { + runTest(document.getElementById('checkSchedulerAPI'), () => { + if ( + typeof ReactScheduler === 'undefined' || + typeof ReactScheduler.unstable_now !== 'function' || + typeof ReactScheduler.unstable_scheduleWork !== 'function' || + typeof ReactScheduler.unstable_cancelScheduledWork !== 'function' + ) { + throw 'API is not defined'; + } + + if (ReactScheduler.unstable_now() !== performance.now()) { + throw 'API does not work'; + } + + // There is no real way to verify that the two APIs are connected. + }); +} + +function checkSchedulerTrackingAPI() { + runTest(document.getElementById('checkSchedulerTrackingAPI'), () => { + if ( + typeof ReactSchedulerTracking === 'undefined' || + typeof ReactSchedulerTracking.__getInteractionsRef !== 'function' || + typeof ReactSchedulerTracking.__getSubscriberRef !== 'function' || + typeof ReactSchedulerTracking.unstable_clear !== 'function' || + typeof ReactSchedulerTracking.unstable_getCurrent !== 'function' || + typeof ReactSchedulerTracking.unstable_getThreadID !== 'function' || + typeof ReactSchedulerTracking.unstable_track !== 'function' || + typeof ReactSchedulerTracking.unstable_wrap !== 'function' + ) { + throw 'API is not defined'; + } + + try { + let interactionsSet; + ReactSchedulerTracking.unstable_track('test', 123, () => { + interactionsSet = ReactSchedulerTracking.__getInteractionsRef().current; + }); + if (interactionsSet.size !== 1) { + throw null; + } + const interaction = Array.from(interactionsSet)[0]; + if (interaction.name !== 'test' || interaction.timestamp !== 123) { + throw null; + } + } catch (error) { + throw 'API does not work'; + } + + const ForwardedSchedulerTracking = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .SchedulerTracking; + + if ( + ReactSchedulerTracking.unstable_getThreadID() === + ForwardedSchedulerTracking.unstable_getThreadID() + ) { + throw 'API forwarding is broken'; + } + }); +} + +function checkSchedulerTrackingSubscriptionsAPI() { + runTest( + document.getElementById('checkSchedulerTrackingSubscriptionsAPI'), + () => { + if ( + typeof ReactSchedulerTracking === 'undefined' || + typeof ReactSchedulerTracking.unstable_subscribe !== 'function' || + typeof ReactSchedulerTracking.unstable_unsubscribe !== 'function' + ) { + throw 'API is not defined'; + } + + const onInteractionScheduledWorkCompletedCalls = []; + const onInteractionTrackedCalls = []; + const onWorkCanceledCalls = []; + const onWorkScheduledCalls = []; + const onWorkStartedCalls = []; + const onWorkStoppedCalls = []; + const subscriber = { + onInteractionScheduledWorkCompleted: (...args) => + onInteractionScheduledWorkCompletedCalls.push(args), + onInteractionTracked: (...args) => onInteractionTrackedCalls.push(args), + onWorkCanceled: (...args) => onWorkCanceledCalls.push(args), + onWorkScheduled: (...args) => onWorkScheduledCalls.push(args), + onWorkStarted: (...args) => onWorkStartedCalls.push(args), + onWorkStopped: (...args) => onWorkStoppedCalls.push(args), + }; + + try { + ReactSchedulerTracking.unstable_subscribe(subscriber); + ReactSchedulerTracking.unstable_track('foo', 123, () => {}); + ReactSchedulerTracking.unstable_unsubscribe(subscriber); + if (onInteractionTrackedCalls.length !== 1) { + throw null; + } + const interaction = onInteractionTrackedCalls[0][0]; + if (interaction.name !== 'foo' || interaction.timestamp !== 123) { + throw null; + } + ReactSchedulerTracking.unstable_track('bar', 456, () => {}); + if (onInteractionTrackedCalls.length !== 1) { + throw null; + } + } catch (error) { + throw 'API does not forward methods'; + } + + const ForwardedSchedulerTracking = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .SchedulerTracking; + + try { + ForwardedSchedulerTracking.unstable_subscribe(subscriber); + ReactSchedulerTracking.unstable_track('foo', 123, () => {}); + ForwardedSchedulerTracking.unstable_track('bar', 456, () => {}); + ReactSchedulerTracking.unstable_unsubscribe(subscriber); + if (onInteractionTrackedCalls.length !== 3) { + throw null; + } + const interactionFoo = onInteractionTrackedCalls[1][0]; + const interactionBar = onInteractionTrackedCalls[2][0]; + if ( + interactionFoo.name !== 'foo' || + interactionFoo.timestamp !== 123 || + interactionBar.name !== 'bar' || + interactionBar.timestamp !== 456 + ) { + throw null; + } + ForwardedSchedulerTracking.unstable_track('baz', 789, () => {}); + if (onInteractionTrackedCalls.length !== 3) { + throw null; + } + } catch (error) { + throw 'API forwarding is broken'; + } + } + ); +} + +function checkEndToEndIntegration() { + runTest(document.getElementById('checkEndToEndIntegration'), () => { + try { + const onRenderCalls = []; + const onRender = (...args) => onRenderCalls.push(args); + const container = document.createElement('div'); + + ReactSchedulerTracking.unstable_track('render', 123, () => { + ReactDOM.render( + React.createElement( + React.unstable_Profiler, + {id: 'profiler', onRender}, + React.createElement('div', null, 'hi') + ), + container + ); + }); + + if (container.textContent !== 'hi') { + throw null; + } + + if (onRenderCalls.length !== 1) { + throw null; + } + const call = onRenderCalls[0]; + if (call.length !== 7) { + throw null; + } + const interaction = Array.from(call[6])[0]; + if (interaction.name !== 'render' || interaction.timestamp !== 123) { + throw null; + } + } catch (error) { + throw 'End to end integration is broken'; + } + }); +} diff --git a/fixtures/unstable-async/suspense/package.json b/fixtures/unstable-async/suspense/package.json index e2364a2c63965..1b6f96186692d 100644 --- a/fixtures/unstable-async/suspense/package.json +++ b/fixtures/unstable-async/suspense/package.json @@ -10,10 +10,10 @@ "dependencies": { "clipboard": "^1.7.1", "github-fork-ribbon-css": "^0.2.1", - "interaction-tracking": "../../../build/node_modules/interaction-tracking", "react": "../../../build/node_modules/react", "react-dom": "../../../build/node_modules/react-dom", "react-draggable": "^3.0.5", + "react-scheduler": "../../../build/node_modules/react-scheduler", "simple-cache-provider": "../../../build/node_modules/simple-cache-provider" }, "scripts": { diff --git a/fixtures/unstable-async/suspense/src/components/App.js b/fixtures/unstable-async/suspense/src/components/App.js index 88e0cd011ff99..f40506d51afd4 100644 --- a/fixtures/unstable-async/suspense/src/components/App.js +++ b/fixtures/unstable-async/suspense/src/components/App.js @@ -1,5 +1,8 @@ -import {track, wrap} from 'interaction-tracking'; import React, {Placeholder, PureComponent} from 'react'; +import { + unstable_track as track, + unstable_wrap as wrap, +} from 'react-scheduler/tracking'; import {createResource} from 'simple-cache-provider'; import {cache} from '../cache'; import Spinner from './Spinner'; diff --git a/fixtures/unstable-async/suspense/src/index.js b/fixtures/unstable-async/suspense/src/index.js index a2c047e810a1a..1a6a82feb14ad 100644 --- a/fixtures/unstable-async/suspense/src/index.js +++ b/fixtures/unstable-async/suspense/src/index.js @@ -1,6 +1,6 @@ -import {track} from 'interaction-tracking'; import React, {Fragment, PureComponent} from 'react'; import {unstable_createRoot, render} from 'react-dom'; +import {unstable_track as track} from 'react-scheduler/tracking'; import {cache} from './cache'; import { setFakeRequestTime, diff --git a/fixtures/unstable-async/suspense/yarn.lock b/fixtures/unstable-async/suspense/yarn.lock index f80f812083ff8..5b2ffc6706c04 100644 --- a/fixtures/unstable-async/suspense/yarn.lock +++ b/fixtures/unstable-async/suspense/yarn.lock @@ -3482,9 +3482,6 @@ inquirer@3.3.0, inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -interaction-tracking@../../../build/node_modules/interaction-tracking: - version "0.0.1" - internal-ip@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" @@ -4102,6 +4099,10 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + js-yaml@^3.4.3, js-yaml@^3.7.0, js-yaml@^3.9.1: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" @@ -4389,12 +4390,18 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: js-tokens "^3.0.0" +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -5720,11 +5727,12 @@ react-dev-utils@^5.0.1: text-table "0.2.0" react-dom@../../../build/node_modules/react-dom: - version "16.4.1" + version "16.4.3-alpha.0" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" + react-scheduler "^0.1.0-alpha-1" react-draggable@^3.0.5: version "3.0.5" @@ -5737,6 +5745,15 @@ react-error-overlay@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4" +react-scheduler@../../../build/node_modules/react-scheduler: + version "0.1.0-alpha-1" + dependencies: + object-assign "^4.1.1" + +react-scheduler@^0.1.0-alpha-1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/react-scheduler/-/react-scheduler-0.1.0.tgz#d16c0ee90d2895ff1941e0d681293dbe4f4b4f38" + react-scripts@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-1.1.4.tgz#d5c230e707918d6dd2d06f303b10f5222d017c88" @@ -5783,11 +5800,12 @@ react-scripts@^1.1.4: fsevents "^1.1.3" react@../../../build/node_modules/react: - version "16.4.1" + version "16.4.3-alpha.0" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" + react-scheduler "^0.1.0-alpha-1" read-pkg-up@^1.0.1: version "1.0.1" diff --git a/packages/interaction-tracking/README.md b/packages/interaction-tracking/README.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/packages/interaction-tracking/index.js b/packages/interaction-tracking/index.js deleted file mode 100644 index ee8856443dfa3..0000000000000 --- a/packages/interaction-tracking/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -'use strict'; - -export * from './src/InteractionTracking'; diff --git a/packages/interaction-tracking/npm/index.js b/packages/interaction-tracking/npm/index.js deleted file mode 100644 index 6f7a570506f7d..0000000000000 --- a/packages/interaction-tracking/npm/index.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/interaction-tracking.production.min.js'); -} else { - module.exports = require('./cjs/interaction-tracking.development.js'); -} diff --git a/packages/interaction-tracking/package.json b/packages/interaction-tracking/package.json deleted file mode 100644 index 2bb9bb4e780c5..0000000000000 --- a/packages/interaction-tracking/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "interaction-tracking", - "description": "utility for tracking interaction events", - "version": "0.0.1", - "license": "MIT", - "files": [ - "LICENSE", - "README.md", - "index.js", - "subscriptions.js", - "cjs/", - "umd/" - ], - "keywords": [ - "interaction", - "tracking", - "react" - ], - "repository": "https://github.com/facebook/react", - "bugs": { - "url": "https://github.com/facebook/react/issues" - }, - "homepage": "https://reactjs.org/" -} diff --git a/packages/react-art/package.json b/packages/react-art/package.json index a33b1e4823372..8b5818de97151 100644 --- a/packages/react-art/package.json +++ b/packages/react-art/package.json @@ -22,7 +22,8 @@ "create-react-class": "^15.6.2", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-scheduler": "^0.1.0-alpha-1" }, "peerDependencies": { "react": "^16.0.0" diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js index e6ce862766fb0..a904477cc33c7 100644 --- a/packages/react-art/src/ReactARTHostConfig.js +++ b/packages/react-art/src/ReactARTHostConfig.js @@ -5,7 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import * as ReactScheduler from 'shared/ReactScheduler'; +export { + unstable_now as now, + unstable_scheduleWork as scheduleDeferredCallback, + unstable_cancelScheduledWork as cancelDeferredCallback, +} from 'react-scheduler'; import Transform from 'art/core/transform'; import Mode from 'art/modes/current'; import invariant from 'shared/invariant'; @@ -329,9 +333,6 @@ export function getChildHostContext() { return NO_CONTEXT; } -export const scheduleDeferredCallback = ReactScheduler.scheduleWork; -export const cancelDeferredCallback = ReactScheduler.cancelScheduledWork; - export const scheduleTimeout = setTimeout; export const cancelTimeout = clearTimeout; export const noTimeout = -1; @@ -342,8 +343,6 @@ export function shouldSetTextContent(type, props) { ); } -export const now = ReactScheduler.now; - // The ART renderer is secondary to the React DOM renderer. export const isPrimaryRenderer = false; diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index 9f89e24c767da..4f587172cb975 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -15,7 +15,8 @@ "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-scheduler": "^0.1.0-alpha-1" }, "peerDependencies": { "react": "^16.0.0" diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 4d74569760ca1..8a5c0578a8229 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -61,10 +61,10 @@ export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; export { - now, - scheduleWork as scheduleDeferredCallback, - cancelScheduledWork as cancelDeferredCallback, -} from 'shared/ReactScheduler'; + unstable_now as now, + unstable_scheduleWork as scheduleDeferredCallback, + unstable_cancelScheduledWork as cancelDeferredCallback, +} from 'react-scheduler'; let SUPPRESS_HYDRATION_WARNING; if (__DEV__) { diff --git a/packages/react-native-renderer/package.json b/packages/react-native-renderer/package.json index e50924169b848..5aa9a77937a34 100644 --- a/packages/react-native-renderer/package.json +++ b/packages/react-native-renderer/package.json @@ -5,7 +5,8 @@ "repository": "facebook/react", "dependencies": { "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-scheduler": "^0.1.0-alpha-1" }, "peerDependencies": { "react": "^16.0.0" diff --git a/packages/react-reconciler/package.json b/packages/react-reconciler/package.json index c6b033176a0fb..66c7dc53a9796 100644 --- a/packages/react-reconciler/package.json +++ b/packages/react-reconciler/package.json @@ -27,7 +27,8 @@ "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-scheduler": "^0.1.0-alpha-1" }, "browserify": { "transform": [ diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 11317e99cf7b1..e677c62725ed8 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -20,7 +20,7 @@ import type {ExpirationTime} from './ReactFiberExpirationTime'; import type {CapturedValue, CapturedError} from './ReactCapturedValue'; import { - enableInteractionTracking, + enableSchedulerTracking, enableProfilerTimer, enableSuspense, } from 'shared/ReactFeatureFlags'; @@ -845,7 +845,7 @@ function commitWork( if (enableProfilerTimer) { const onRender = finishedWork.memoizedProps.onRender; - if (enableInteractionTracking) { + if (enableSchedulerTracking) { onRender( finishedWork.memoizedProps.id, current === null ? 'mount' : 'update', diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js index 2057ca6d59644..427e4ac8704ab 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.js +++ b/packages/react-reconciler/src/ReactFiberRoot.js @@ -10,13 +10,13 @@ import type {Fiber} from './ReactFiber'; import type {ExpirationTime} from './ReactFiberExpirationTime'; import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; -import type {Interaction} from 'interaction-tracking/src/InteractionTracking'; +import type {Interaction} from 'react-scheduler/src/Tracking'; import {noTimeout} from './ReactFiberHostConfig'; import {createHostRootFiber} from './ReactFiber'; import {NoWork} from './ReactFiberExpirationTime'; -import {enableInteractionTracking} from 'shared/ReactFeatureFlags'; -import {getThreadID} from 'interaction-tracking'; +import {enableSchedulerTracking} from 'shared/ReactFeatureFlags'; +import {unstable_getThreadID} from 'react-scheduler/tracking'; // TODO: This should be lifted into the renderer. export type Batch = { @@ -82,7 +82,7 @@ type BaseFiberRootProperties = {| // The following attributes are only used by interaction tracking builds. // They enable interactions to be associated with their async work, // And expose interaction metadata to the React DevTools Profiler plugin. -// Note that these attributes are only defined when the enableInteractionTracking flag is enabled. +// Note that these attributes are only defined when the enableSchedulerTracking flag is enabled. type ProfilingOnlyFiberRootProperties = {| interactionThreadID: number, memoizedInteractions: Set, @@ -91,9 +91,9 @@ type ProfilingOnlyFiberRootProperties = {| // Exported FiberRoot type includes all properties, // To avoid requiring potentially error-prone :any casts throughout the project. -// Profiling properties are only safe to access in profiling builds (when enableInteractionTracking is true). +// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracking is true). // The types are defined separately within this file to ensure they stay in sync. -// (We don't have to use an inline :any cast when enableInteractionTracking is disabled.) +// (We don't have to use an inline :any cast when enableSchedulerTracking is disabled.) export type FiberRoot = { ...BaseFiberRootProperties, ...ProfilingOnlyFiberRootProperties, @@ -109,7 +109,7 @@ export function createFiberRoot( const uninitializedFiber = createHostRootFiber(isAsync); let root; - if (enableInteractionTracking) { + if (enableSchedulerTracking) { root = ({ current: uninitializedFiber, containerInfo: containerInfo, @@ -134,7 +134,7 @@ export function createFiberRoot( firstBatch: null, nextScheduledRoot: null, - interactionThreadID: getThreadID(), + interactionThreadID: unstable_getThreadID(), memoizedInteractions: new Set(), pendingInteractionMap: new Map(), }: FiberRoot); @@ -168,8 +168,8 @@ export function createFiberRoot( uninitializedFiber.stateNode = root; // The reason for the way the Flow types are structured in this file, - // Is to avoid needing :any casts everywhere interaction-tracking fields are used. - // Unfortunately that requires an :any cast for non-interaction-tracking capable builds. + // Is to avoid needing :any casts everywhere interaction tracking fields are used. + // Unfortunately that requires an :any cast for non-interaction tracking capable builds. // $FlowFixMe Remove this :any cast and replace it with something better. return ((root: any): FiberRoot); } diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js index 9f053d6cece60..dd27947cc42bf 100644 --- a/packages/react-reconciler/src/ReactFiberScheduler.js +++ b/packages/react-reconciler/src/ReactFiberScheduler.js @@ -10,9 +10,16 @@ import type {Fiber} from './ReactFiber'; import type {Batch, FiberRoot} from './ReactFiberRoot'; import type {ExpirationTime} from './ReactFiberExpirationTime'; -import type {Interaction} from 'interaction-tracking/src/InteractionTracking'; +import type { + Interaction, + InteractionsRef, + SubscriberRef, +} from 'react-scheduler/src/Tracking'; -import {__interactionsRef, __subscriberRef} from 'interaction-tracking'; +import { + __getInteractionsRef, + __getSubscriberRef, +} from 'react-scheduler/tracking'; import { invokeGuardedCallback, hasCaughtError, @@ -44,7 +51,7 @@ import { HostPortal, } from 'shared/ReactWorkTags'; import { - enableInteractionTracking, + enableSchedulerTracking, enableProfilerTimer, enableUserTimingAPI, replayFailedUnitOfWorkWithInvokeGuardedCallback, @@ -162,6 +169,13 @@ export type Thenable = { const {ReactCurrentOwner} = ReactSharedInternals; +let interactionsRef: InteractionsRef = (null: any); +let subscriberRef: SubscriberRef = (null: any); +if (enableSchedulerTracking) { + interactionsRef = __getInteractionsRef(); + subscriberRef = __getSubscriberRef(); +} + let didWarnAboutStateTransition; let didWarnSetStateChildContext; let warnAboutUpdateOnUnmounted; @@ -553,14 +567,14 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void { markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit); let prevInteractions: Set = (null: any); - let committedInteractions: Array = enableInteractionTracking + let committedInteractions: Array = enableSchedulerTracking ? [] : (null: any); - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // Restore any pending interactions at this point, // So that cascading work triggered during the render phase will be accounted for. - prevInteractions = __interactionsRef.current; - __interactionsRef.current = root.memoizedInteractions; + prevInteractions = interactionsRef.current; + interactionsRef.current = root.memoizedInteractions; // We are potentially finished with the current batch of interactions. // So we should clear them out of the pending interaction map. @@ -754,13 +768,13 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void { } onCommit(root, earliestRemainingTimeAfterCommit); - if (enableInteractionTracking) { - __interactionsRef.current = prevInteractions; + if (enableSchedulerTracking) { + interactionsRef.current = prevInteractions; let subscriber; try { - subscriber = __subscriberRef.current; + subscriber = subscriberRef.current; if (subscriber !== null && root.memoizedInteractions.size > 0) { const threadID = computeThreadID( committedExpirationTime, @@ -1162,11 +1176,11 @@ function renderRoot( const expirationTime = root.nextExpirationTimeToWorkOn; let prevInteractions: Set = (null: any); - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // We're about to start new tracked work. // Restore pending interactions so cascading work triggered during the render phase will be accounted for. - prevInteractions = __interactionsRef.current; - __interactionsRef.current = root.memoizedInteractions; + prevInteractions = interactionsRef.current; + interactionsRef.current = root.memoizedInteractions; } // Check if we're starting from a fresh stack, or if we're resuming from @@ -1187,7 +1201,7 @@ function renderRoot( ); root.pendingCommitExpirationTime = NoWork; - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // Determine which interactions this batch of work currently includes, // So that we can accurately attribute time spent working on it, // And so that cascading work triggered during the render phase will be associated with it. @@ -1209,7 +1223,7 @@ function renderRoot( root.memoizedInteractions = interactions; if (interactions.size > 0) { - const subscriber = __subscriberRef.current; + const subscriber = subscriberRef.current; if (subscriber !== null) { const threadID = computeThreadID( expirationTime, @@ -1218,7 +1232,7 @@ function renderRoot( try { subscriber.onWorkStarted(interactions, threadID); } catch (error) { - // Work thrown by a interaction-tracking subscriber should be rethrown, + // Work thrown by an interaction tracking subscriber should be rethrown, // But only once it's safe (to avoid leaveing the scheduler in an invalid state). // Store the error for now and we'll re-throw in finishRendering(). if (!hasUnhandledError) { @@ -1292,9 +1306,9 @@ function renderRoot( break; } while (true); - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // Tracked work is done for now; restore the previous interactions. - __interactionsRef.current = prevInteractions; + interactionsRef.current = prevInteractions; } // We're done performing work. Time to clean up. @@ -1601,15 +1615,15 @@ function retrySuspendedRoot( scheduleWorkToRoot(fiber, retryTime); const rootExpirationTime = root.expirationTime; if (rootExpirationTime !== NoWork) { - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // Restore previous interactions so that new work is associated with them. - let prevInteractions = __interactionsRef.current; - __interactionsRef.current = root.memoizedInteractions; + let prevInteractions = interactionsRef.current; + interactionsRef.current = root.memoizedInteractions; // Because suspense timeouts do not decrement the interaction count, // Continued suspense work should also not increment the count. storeInteractionsForExpirationTime(root, rootExpirationTime, false); requestWork(root, rootExpirationTime); - __interactionsRef.current = prevInteractions; + interactionsRef.current = prevInteractions; } else { requestWork(root, rootExpirationTime); } @@ -1672,11 +1686,11 @@ function storeInteractionsForExpirationTime( expirationTime: ExpirationTime, updateInteractionCounts: boolean, ): void { - if (!enableInteractionTracking) { + if (!enableSchedulerTracking) { return; } - const interactions = __interactionsRef.current; + const interactions = interactionsRef.current; if (interactions.size > 0) { const pendingInteractions = root.pendingInteractionMap.get(expirationTime); if (pendingInteractions != null) { @@ -1699,7 +1713,7 @@ function storeInteractionsForExpirationTime( } } - const subscriber = __subscriberRef.current; + const subscriber = subscriberRef.current; if (subscriber !== null) { const threadID = computeThreadID( expirationTime, @@ -1731,7 +1745,7 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) { return; } - if (enableInteractionTracking) { + if (enableSchedulerTracking) { storeInteractionsForExpirationTime(root, expirationTime, true); } @@ -1922,7 +1936,7 @@ function onTimeout(root, finishedWork, suspendedExpirationTime) { recomputeCurrentRendererTime(); currentSchedulerTime = currentRendererTime; - if (enableInteractionTracking) { + if (enableSchedulerTracking) { // Don't update pending interaction counts for suspense timeouts, // Because we know we still need to do more work in this case. suspenseDidTimeout = true; diff --git a/packages/react-scheduler/npm/tracking.js b/packages/react-scheduler/npm/tracking.js new file mode 100644 index 0000000000000..997edc5a81389 --- /dev/null +++ b/packages/react-scheduler/npm/tracking.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react-scheduler-tracking.production.min.js'); +} else { + module.exports = require('./cjs/react-scheduler-tracking.development.js'); +} diff --git a/packages/react-scheduler/npm/umd/react-scheduler-tracking.js b/packages/react-scheduler/npm/umd/react-scheduler-tracking.js new file mode 100644 index 0000000000000..d237e3bd820b2 --- /dev/null +++ b/packages/react-scheduler/npm/umd/react-scheduler-tracking.js @@ -0,0 +1,95 @@ +/** + * @license React + * + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? (module.exports = factory(require('react'))) + : typeof define === 'function' && define.amd // eslint-disable-line no-undef + ? define(['react'], factory) // eslint-disable-line no-undef + : (global.ReactSchedulerTracking = factory(global)); +})(this, function(global) { + function __getInteractionsRef() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.__getInteractionsRef.apply( + this, + arguments + ); + } + + function __getSubscriberRef() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.__getSubscriberRef.apply( + this, + arguments + ); + } + + function unstable_clear() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_clear.apply( + this, + arguments + ); + } + + function unstable_getCurrent() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_getCurrent.apply( + this, + arguments + ); + } + + function unstable_getThreadID() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_getThreadID.apply( + this, + arguments + ); + } + + function unstable_subscribe() { + // eslint-disable-next-line max-len + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_subscribe.apply( + this, + arguments + ); + } + + function unstable_track() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_track.apply( + this, + arguments + ); + } + + function unstable_unsubscribe() { + // eslint-disable-next-line max-len + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_unsubscribe.apply( + this, + arguments + ); + } + + function unstable_wrap() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.SchedulerTracking.unstable_wrap.apply( + this, + arguments + ); + } + + return Object.freeze({ + __getInteractionsRef: __getInteractionsRef, + __getSubscriberRef: __getSubscriberRef, + unstable_clear: unstable_clear, + unstable_getCurrent: unstable_getCurrent, + unstable_getThreadID: unstable_getThreadID, + unstable_subscribe: unstable_subscribe, + unstable_track: unstable_track, + unstable_unsubscribe: unstable_unsubscribe, + unstable_wrap: unstable_wrap, + }); +}); diff --git a/packages/react-scheduler/npm/umd/react-scheduler.js b/packages/react-scheduler/npm/umd/react-scheduler.js new file mode 100644 index 0000000000000..f02ccba6bfbd3 --- /dev/null +++ b/packages/react-scheduler/npm/umd/react-scheduler.js @@ -0,0 +1,45 @@ +/** + * @license React + * + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? (module.exports = factory(require('react'))) + : typeof define === 'function' && define.amd // eslint-disable-line no-undef + ? define(['react'], factory) // eslint-disable-line no-undef + : (global.ReactScheduler = factory(global)); +})(this, function(global) { + function unstable_now() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_now.apply( + this, + arguments + ); + } + + function unstable_scheduleWork() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_scheduleWork.apply( + this, + arguments + ); + } + + function unstable_cancelScheduledWork() { + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_cancelScheduledWork.apply( + this, + arguments + ); + } + + return Object.freeze({ + unstable_now: unstable_now, + unstable_scheduleWork: unstable_scheduleWork, + unstable_cancelScheduledWork: unstable_cancelScheduledWork, + }); +}); diff --git a/packages/react-scheduler/package.json b/packages/react-scheduler/package.json index 633f48859bcd6..3daf0c475e5ed 100644 --- a/packages/react-scheduler/package.json +++ b/packages/react-scheduler/package.json @@ -20,6 +20,7 @@ "LICENSE", "README.md", "index.js", + "tracking.js", "cjs/", "umd/" ] diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index 550d064a9b82d..aff461781c852 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -465,4 +465,8 @@ if (!canUseDOM) { }; } -export {now, scheduleWork, cancelScheduledWork}; +export { + now as unstable_now, + scheduleWork as unstable_scheduleWork, + cancelScheduledWork as unstable_cancelScheduledWork, +}; diff --git a/packages/interaction-tracking/src/InteractionTracking.js b/packages/react-scheduler/src/Tracking.js similarity index 83% rename from packages/interaction-tracking/src/InteractionTracking.js rename to packages/react-scheduler/src/Tracking.js index 7b69678b19dbd..6678ad3cc9a1d 100644 --- a/packages/interaction-tracking/src/InteractionTracking.js +++ b/packages/react-scheduler/src/Tracking.js @@ -7,7 +7,7 @@ * @flow */ -import {enableInteractionTracking} from 'shared/ReactFeatureFlags'; +import {enableSchedulerTracking} from 'shared/ReactFeatureFlags'; export type Interaction = {| __count: number, @@ -70,7 +70,7 @@ let interactionsRef: InteractionsRef = (null: any); // Listener(s) to notify when interactions begin and end. let subscriberRef: SubscriberRef = (null: any); -if (enableInteractionTracking) { +if (enableSchedulerTracking) { interactionsRef = { current: new Set(), }; @@ -81,10 +81,16 @@ if (enableInteractionTracking) { // These values are exported for libraries with advanced use cases (i.e. React). // They should not typically be accessed directly. -export {interactionsRef as __interactionsRef, subscriberRef as __subscriberRef}; +export function __getInteractionsRef(): InteractionsRef { + return interactionsRef; +} + +export function __getSubscriberRef(): SubscriberRef { + return subscriberRef; +} -export function clear(callback: Function): any { - if (!enableInteractionTracking) { +export function unstable_clear(callback: Function): any { + if (!enableSchedulerTracking) { return callback(); } @@ -98,25 +104,25 @@ export function clear(callback: Function): any { } } -export function getCurrent(): Set | null { - if (!enableInteractionTracking) { +export function unstable_getCurrent(): Set | null { + if (!enableSchedulerTracking) { return null; } else { return interactionsRef.current; } } -export function getThreadID(): number { +export function unstable_getThreadID(): number { return ++threadIDCounter; } -export function track( +export function unstable_track( name: string, timestamp: number, callback: Function, threadID: number = DEFAULT_THREAD_ID, ): any { - if (!enableInteractionTracking) { + if (!enableSchedulerTracking) { return callback(); } @@ -174,11 +180,11 @@ export function track( return returnValue; } -export function wrap( +export function unstable_wrap( callback: Function, threadID: number = DEFAULT_THREAD_ID, ): Function { - if (!enableInteractionTracking) { + if (!enableSchedulerTracking) { return callback; } @@ -195,6 +201,8 @@ export function wrap( interaction.__count++; }); + let hasRun = false; + function wrapped() { const prevInteractions = interactionsRef.current; interactionsRef.current = wrappedInteractions; @@ -222,16 +230,23 @@ export function wrap( return returnValue; } finally { - // Update pending async counts for all wrapped interactions. - // If this was the last scheduled async work for any of them, - // Mark them as completed. - wrappedInteractions.forEach(interaction => { - interaction.__count--; + if (!hasRun) { + // We only expect a wrapped function to be executed once, + // But in the event that it's executed more than once– + // Only decrement the outstanding interaction counts once. + hasRun = true; + + // Update pending async counts for all wrapped interactions. + // If this was the last scheduled async work for any of them, + // Mark them as completed. + wrappedInteractions.forEach(interaction => { + interaction.__count--; - if (subscriber !== null && interaction.__count === 0) { - subscriber.onInteractionScheduledWorkCompleted(interaction); - } - }); + if (subscriber !== null && interaction.__count === 0) { + subscriber.onInteractionScheduledWorkCompleted(interaction); + } + }); + } } } diff --git a/packages/interaction-tracking/src/InteractionTrackingSubscriptions.js b/packages/react-scheduler/src/TrackingSubscriptions.js similarity index 77% rename from packages/interaction-tracking/src/InteractionTrackingSubscriptions.js rename to packages/react-scheduler/src/TrackingSubscriptions.js index da53178f16113..4c45af957ad1d 100644 --- a/packages/interaction-tracking/src/InteractionTrackingSubscriptions.js +++ b/packages/react-scheduler/src/TrackingSubscriptions.js @@ -7,25 +7,42 @@ * @flow */ -import type {Interaction, Subscriber} from './InteractionTracking'; +import type {Interaction, Subscriber, SubscriberRef} from './Tracking'; -import {enableInteractionTracking} from 'shared/ReactFeatureFlags'; -import {__subscriberRef} from 'interaction-tracking'; +import {enableSchedulerTracking} from 'shared/ReactFeatureFlags'; +import {__getSubscriberRef} from 'react-scheduler/tracking'; +let subscriberRef: SubscriberRef = (null: any); let subscribers: Set = (null: any); -if (enableInteractionTracking) { +if (enableSchedulerTracking) { + subscriberRef = __getSubscriberRef(); subscribers = new Set(); } -export function subscribe(subscriber: Subscriber): void { - if (enableInteractionTracking) { +export function unstable_subscribe(subscriber: Subscriber): void { + if (enableSchedulerTracking) { subscribers.add(subscriber); + + if (subscribers.size === 1) { + subscriberRef.current = { + onInteractionScheduledWorkCompleted, + onInteractionTracked, + onWorkCanceled, + onWorkScheduled, + onWorkStarted, + onWorkStopped, + }; + } } } -export function unsubscribe(subscriber: Subscriber): void { - if (enableInteractionTracking) { +export function unstable_unsubscribe(subscriber: Subscriber): void { + if (enableSchedulerTracking) { subscribers.delete(subscriber); + + if (subscribers.size === 0) { + subscriberRef.current = null; + } } } @@ -154,14 +171,3 @@ function onWorkCanceled( throw caughtError; } } - -if (enableInteractionTracking) { - __subscriberRef.current = { - onInteractionScheduledWorkCompleted, - onInteractionTracked, - onWorkCanceled, - onWorkScheduled, - onWorkStarted, - onWorkStopped, - }; -} diff --git a/packages/react-scheduler/src/__tests__/ReactScheduler-test.js b/packages/react-scheduler/src/__tests__/ReactScheduler-test.js index a1070dbff67e6..3577daf1877c3 100644 --- a/packages/react-scheduler/src/__tests__/ReactScheduler-test.js +++ b/packages/react-scheduler/src/__tests__/ReactScheduler-test.js @@ -99,7 +99,7 @@ describe('ReactScheduler', () => { describe('scheduleWork', () => { it('calls the callback within the frame when not blocked', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const cb = jest.fn(); scheduleWork(cb); advanceOneFrame({timeLeftInFrame: 15}); @@ -111,7 +111,7 @@ describe('ReactScheduler', () => { describe('with multiple callbacks', () => { it('accepts multiple callbacks and calls within frame when not blocked', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => callbackLog.push('A')); const callbackB = jest.fn(() => callbackLog.push('B')); @@ -137,7 +137,7 @@ describe('ReactScheduler', () => { }); it("accepts callbacks betweeen animationFrame and postMessage and doesn't stall", () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => callbackLog.push('A')); const callbackB = jest.fn(() => callbackLog.push('B')); @@ -167,7 +167,7 @@ describe('ReactScheduler', () => { 'schedules callbacks in correct order and' + 'keeps calling them if there is time', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => { callbackLog.push('A'); @@ -194,7 +194,7 @@ describe('ReactScheduler', () => { ); it('schedules callbacks in correct order when callbacks have many nested scheduleWork calls', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => { callbackLog.push('A'); @@ -229,7 +229,7 @@ describe('ReactScheduler', () => { }); it('schedules callbacks in correct order when they use scheduleWork to schedule themselves', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; let callbackAIterations = 0; const callbackA = jest.fn(() => { @@ -260,7 +260,7 @@ describe('ReactScheduler', () => { describe('when there is no more time left in the frame', () => { it('calls any callback which has timed out, waits for others', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; startOfLatestFrame = 1000000000000; currentTime = startOfLatestFrame - 10; const callbackLog = []; @@ -295,7 +295,7 @@ describe('ReactScheduler', () => { describe('when there is some time left in the frame', () => { it('calls timed out callbacks and then any more pending callbacks, defers others if time runs out', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; startOfLatestFrame = 1000000000000; currentTime = startOfLatestFrame - 10; const callbackLog = []; @@ -343,7 +343,10 @@ describe('ReactScheduler', () => { describe('cancelScheduledWork', () => { it('cancels the scheduled callback', () => { - const {scheduleWork, cancelScheduledWork} = ReactScheduler; + const { + unstable_scheduleWork: scheduleWork, + unstable_cancelScheduledWork: cancelScheduledWork, + } = ReactScheduler; const cb = jest.fn(); const callbackId = scheduleWork(cb); expect(cb).toHaveBeenCalledTimes(0); @@ -354,7 +357,10 @@ describe('ReactScheduler', () => { describe('with multiple callbacks', () => { it('when called more than once', () => { - const {scheduleWork, cancelScheduledWork} = ReactScheduler; + const { + unstable_scheduleWork: scheduleWork, + unstable_cancelScheduledWork: cancelScheduledWork, + } = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => callbackLog.push('A')); const callbackB = jest.fn(() => callbackLog.push('B')); @@ -375,7 +381,10 @@ describe('ReactScheduler', () => { }); it('when one callback cancels the next one', () => { - const {scheduleWork, cancelScheduledWork} = ReactScheduler; + const { + unstable_scheduleWork: scheduleWork, + unstable_cancelScheduledWork: cancelScheduledWork, + } = ReactScheduler; const callbackLog = []; let callbackBId; const callbackA = jest.fn(() => { @@ -412,7 +421,7 @@ describe('ReactScheduler', () => { * */ it('still calls all callbacks within same frame', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => callbackLog.push('A')); const callbackB = jest.fn(() => { @@ -458,7 +467,7 @@ describe('ReactScheduler', () => { * */ it('and with some timed out callbacks, still calls all callbacks within same frame', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => { callbackLog.push('A'); @@ -504,7 +513,7 @@ describe('ReactScheduler', () => { * */ it('still calls all callbacks within same frame', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => { callbackLog.push('A'); @@ -565,7 +574,7 @@ describe('ReactScheduler', () => { * */ it('and with all timed out callbacks, still calls all callbacks within same frame', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; const callbackLog = []; const callbackA = jest.fn(() => { callbackLog.push('A'); @@ -646,7 +655,7 @@ describe('ReactScheduler', () => { * */ it('still calls all callbacks within same frame', () => { - const {scheduleWork} = ReactScheduler; + const {unstable_scheduleWork: scheduleWork} = ReactScheduler; startOfLatestFrame = 1000000000000; currentTime = startOfLatestFrame - 10; catchPostMessageErrors = true; diff --git a/packages/interaction-tracking/src/__tests__/InteractionTracking-test.internal.js b/packages/react-scheduler/src/__tests__/Tracking-test.internal.js similarity index 55% rename from packages/interaction-tracking/src/__tests__/InteractionTracking-test.internal.js rename to packages/react-scheduler/src/__tests__/Tracking-test.internal.js index 0b26591315d1c..9b0e0d7108330 100644 --- a/packages/interaction-tracking/src/__tests__/InteractionTracking-test.internal.js +++ b/packages/react-scheduler/src/__tests__/Tracking-test.internal.js @@ -8,14 +8,14 @@ */ 'use strict'; -describe('InteractionTracking', () => { - let InteractionTracking; +describe('Tracking', () => { + let SchedulerTracking; let ReactFeatureFlags; let advanceTimeBy; let currentTime; - function loadModules({enableInteractionTracking}) { + function loadModules({enableSchedulerTracking}) { jest.resetModules(); jest.useFakeTimers(); @@ -27,36 +27,36 @@ describe('InteractionTracking', () => { }; ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableInteractionTracking = enableInteractionTracking; + ReactFeatureFlags.enableSchedulerTracking = enableSchedulerTracking; - InteractionTracking = require('interaction-tracking'); + SchedulerTracking = require('react-scheduler/tracking'); } - describe('enableInteractionTracking enabled', () => { - beforeEach(() => loadModules({enableInteractionTracking: true})); + describe('enableSchedulerTracking enabled', () => { + beforeEach(() => loadModules({enableSchedulerTracking: true})); it('should return the value of a tracked function', () => { expect( - InteractionTracking.track('arbitrary', currentTime, () => 123), + SchedulerTracking.unstable_track('arbitrary', currentTime, () => 123), ).toBe(123); }); it('should return the value of a clear function', () => { - expect(InteractionTracking.clear(() => 123)).toBe(123); + expect(SchedulerTracking.unstable_clear(() => 123)).toBe(123); }); it('should return the value of a wrapped function', () => { let wrapped; - InteractionTracking.track('arbitrary', currentTime, () => { - wrapped = InteractionTracking.wrap(() => 123); + SchedulerTracking.unstable_track('arbitrary', currentTime, () => { + wrapped = SchedulerTracking.unstable_wrap(() => 123); }); expect(wrapped()).toBe(123); }); it('should pass arguments through to a wrapped function', done => { let wrapped; - InteractionTracking.track('arbitrary', currentTime, () => { - wrapped = InteractionTracking.wrap((param1, param2) => { + SchedulerTracking.unstable_track('arbitrary', currentTime, () => { + wrapped = SchedulerTracking.unstable_wrap((param1, param2) => { expect(param1).toBe('foo'); expect(param2).toBe('bar'); done(); @@ -66,14 +66,14 @@ describe('InteractionTracking', () => { }); it('should return an empty set when outside of a tracked event', () => { - expect(InteractionTracking.getCurrent()).toContainNoInteractions(); + expect(SchedulerTracking.unstable_getCurrent()).toContainNoInteractions(); }); it('should report the tracked interaction from within the track callback', done => { advanceTimeBy(100); - InteractionTracking.track('some event', currentTime, () => { - const interactions = InteractionTracking.getCurrent(); + SchedulerTracking.unstable_track('some event', currentTime, () => { + const interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'some event', timestamp: 100}, ]); @@ -86,7 +86,7 @@ describe('InteractionTracking', () => { let wrappedIndirection; function indirection() { - const interactions = InteractionTracking.getCurrent(); + const interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'some event', timestamp: 100}, ]); @@ -96,8 +96,8 @@ describe('InteractionTracking', () => { advanceTimeBy(100); - InteractionTracking.track('some event', currentTime, () => { - wrappedIndirection = InteractionTracking.wrap(indirection); + SchedulerTracking.unstable_track('some event', currentTime, () => { + wrappedIndirection = SchedulerTracking.unstable_wrap(indirection); }); advanceTimeBy(50); @@ -108,24 +108,26 @@ describe('InteractionTracking', () => { it('should clear the interaction stack for tracked callbacks', () => { let innerTestReached = false; - InteractionTracking.track('outer event', currentTime, () => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + SchedulerTracking.unstable_track('outer event', currentTime, () => { + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event'}, ]); - InteractionTracking.clear(() => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([]); + SchedulerTracking.unstable_clear(() => { + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions( + [], + ); - InteractionTracking.track('inner event', currentTime, () => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([ - {name: 'inner event'}, - ]); + SchedulerTracking.unstable_track('inner event', currentTime, () => { + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions( + [{name: 'inner event'}], + ); innerTestReached = true; }); }); - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event'}, ]); }); @@ -138,29 +140,31 @@ describe('InteractionTracking', () => { let wrappedIndirection; const indirection = jest.fn(() => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event'}, ]); - InteractionTracking.clear(() => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([]); + SchedulerTracking.unstable_clear(() => { + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions( + [], + ); - InteractionTracking.track('inner event', currentTime, () => { - expect(InteractionTracking.getCurrent()).toMatchInteractions([ - {name: 'inner event'}, - ]); + SchedulerTracking.unstable_track('inner event', currentTime, () => { + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions( + [{name: 'inner event'}], + ); innerTestReached = true; }); }); - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event'}, ]); }); - InteractionTracking.track('outer event', currentTime, () => { - wrappedIndirection = InteractionTracking.wrap(indirection); + SchedulerTracking.unstable_track('outer event', currentTime, () => { + wrappedIndirection = SchedulerTracking.unstable_wrap(indirection); }); wrappedIndirection(); @@ -175,7 +179,7 @@ describe('InteractionTracking', () => { let outerIndirectionTracked = false; function innerIndirection() { - const interactions = InteractionTracking.getCurrent(); + const interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'outer event', timestamp: 100}, {name: 'inner event', timestamp: 150}, @@ -185,7 +189,7 @@ describe('InteractionTracking', () => { } function outerIndirection() { - const interactions = InteractionTracking.getCurrent(); + const interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'outer event', timestamp: 100}, ]); @@ -193,16 +197,16 @@ describe('InteractionTracking', () => { outerIndirectionTracked = true; } - InteractionTracking.track('outer event', currentTime, () => { + SchedulerTracking.unstable_track('outer event', currentTime, () => { // Verify the current tracked event - let interactions = InteractionTracking.getCurrent(); + let interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'outer event', timestamp: 100}, ]); advanceTimeBy(50); - const wrapperOuterIndirection = InteractionTracking.wrap( + const wrapperOuterIndirection = SchedulerTracking.unstable_wrap( outerIndirection, ); @@ -210,8 +214,8 @@ describe('InteractionTracking', () => { let innerEventTracked = false; // Verify that a nested event is properly tracked - InteractionTracking.track('inner event', currentTime, () => { - interactions = InteractionTracking.getCurrent(); + SchedulerTracking.unstable_track('inner event', currentTime, () => { + interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'outer event', timestamp: 100}, {name: 'inner event', timestamp: 150}, @@ -221,7 +225,9 @@ describe('InteractionTracking', () => { wrapperOuterIndirection(); expect(outerIndirectionTracked).toBe(true); - wrapperInnerIndirection = InteractionTracking.wrap(innerIndirection); + wrapperInnerIndirection = SchedulerTracking.unstable_wrap( + innerIndirection, + ); innerEventTracked = true; }); @@ -229,7 +235,7 @@ describe('InteractionTracking', () => { expect(innerEventTracked).toBe(true); // Verify that the original event is restored - interactions = InteractionTracking.getCurrent(); + interactions = SchedulerTracking.unstable_getCurrent(); expect(interactions).toMatchInteractions([ {name: 'outer event', timestamp: 100}, ]); @@ -246,14 +252,14 @@ describe('InteractionTracking', () => { it('should reset state appropriately when an error occurs in a track callback', done => { advanceTimeBy(100); - InteractionTracking.track('outer event', currentTime, () => { + SchedulerTracking.unstable_track('outer event', currentTime, () => { expect(() => { - InteractionTracking.track('inner event', currentTime, () => { + SchedulerTracking.unstable_track('inner event', currentTime, () => { throw Error('intentional'); }); }).toThrow(); - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event', timestamp: 100}, ]); @@ -264,18 +270,18 @@ describe('InteractionTracking', () => { it('should reset state appropriately when an error occurs in a wrapped callback', done => { advanceTimeBy(100); - InteractionTracking.track('outer event', currentTime, () => { + SchedulerTracking.unstable_track('outer event', currentTime, () => { let wrappedCallback; - InteractionTracking.track('inner event', currentTime, () => { - wrappedCallback = InteractionTracking.wrap(() => { + SchedulerTracking.unstable_track('inner event', currentTime, () => { + wrappedCallback = SchedulerTracking.unstable_wrap(() => { throw Error('intentional'); }); }); expect(wrappedCallback).toThrow(); - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'outer event', timestamp: 100}, ]); @@ -286,30 +292,30 @@ describe('InteractionTracking', () => { describe('advanced integration', () => { it('should return a unique threadID per request', () => { - expect(InteractionTracking.getThreadID()).not.toBe( - InteractionTracking.getThreadID(), + expect(SchedulerTracking.unstable_getThreadID()).not.toBe( + SchedulerTracking.unstable_getThreadID(), ); }); it('should expose the current set of interactions to be externally manipulated', () => { - InteractionTracking.track('outer event', currentTime, () => { - expect(InteractionTracking.__interactionsRef.current).toBe( - InteractionTracking.getCurrent(), + SchedulerTracking.unstable_track('outer event', currentTime, () => { + expect(SchedulerTracking.__getInteractionsRef().current).toBe( + SchedulerTracking.unstable_getCurrent(), ); - InteractionTracking.__interactionsRef.current = new Set([ + SchedulerTracking.__getInteractionsRef().current = new Set([ {name: 'override event'}, ]); - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ {name: 'override event'}, ]); }); }); it('should expose a subscriber ref to be externally manipulated', () => { - InteractionTracking.track('outer event', currentTime, () => { - expect(InteractionTracking.__subscriberRef).toEqual({ + SchedulerTracking.unstable_track('outer event', currentTime, () => { + expect(SchedulerTracking.__getSubscriberRef()).toEqual({ current: null, }); }); @@ -317,42 +323,42 @@ describe('InteractionTracking', () => { }); }); - describe('enableInteractionTracking disabled', () => { - beforeEach(() => loadModules({enableInteractionTracking: false})); + describe('enableSchedulerTracking disabled', () => { + beforeEach(() => loadModules({enableSchedulerTracking: false})); it('should return the value of a tracked function', () => { expect( - InteractionTracking.track('arbitrary', currentTime, () => 123), + SchedulerTracking.unstable_track('arbitrary', currentTime, () => 123), ).toBe(123); }); it('should return the value of a wrapped function', () => { let wrapped; - InteractionTracking.track('arbitrary', currentTime, () => { - wrapped = InteractionTracking.wrap(() => 123); + SchedulerTracking.unstable_track('arbitrary', currentTime, () => { + wrapped = SchedulerTracking.unstable_wrap(() => 123); }); expect(wrapped()).toBe(123); }); it('should return null for tracked interactions', () => { - expect(InteractionTracking.getCurrent()).toBe(null); + expect(SchedulerTracking.unstable_getCurrent()).toBe(null); }); it('should execute tracked callbacks', done => { - InteractionTracking.track('some event', currentTime, () => { - expect(InteractionTracking.getCurrent()).toBe(null); + SchedulerTracking.unstable_track('some event', currentTime, () => { + expect(SchedulerTracking.unstable_getCurrent()).toBe(null); done(); }); }); it('should return the value of a clear function', () => { - expect(InteractionTracking.clear(() => 123)).toBe(123); + expect(SchedulerTracking.unstable_clear(() => 123)).toBe(123); }); it('should execute wrapped callbacks', done => { - const wrappedCallback = InteractionTracking.wrap(() => { - expect(InteractionTracking.getCurrent()).toBe(null); + const wrappedCallback = SchedulerTracking.unstable_wrap(() => { + expect(SchedulerTracking.unstable_getCurrent()).toBe(null); done(); }); @@ -362,7 +368,7 @@ describe('InteractionTracking', () => { describe('advanced integration', () => { it('should not create unnecessary objects', () => { - expect(InteractionTracking.__interactionsRef).toBe(null); + expect(SchedulerTracking.__getInteractionsRef()).toBe(null); }); }); }); diff --git a/packages/react-scheduler/src/__tests__/Tracking-test.js b/packages/react-scheduler/src/__tests__/Tracking-test.js new file mode 100644 index 0000000000000..ae4638ea31926 --- /dev/null +++ b/packages/react-scheduler/src/__tests__/Tracking-test.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @jest-environment node + */ +'use strict'; + +describe('Tracking', () => { + let SchedulerTracking; + + beforeEach(() => { + jest.resetModules(); + + SchedulerTracking = require('react-scheduler/tracking'); + }); + + it('should return the value of a tracked function', () => { + expect(SchedulerTracking.unstable_track('arbitrary', 0, () => 123)).toBe( + 123, + ); + }); + + it('should return the value of a wrapped function', () => { + let wrapped; + SchedulerTracking.unstable_track('arbitrary', 0, () => { + wrapped = SchedulerTracking.unstable_wrap(() => 123); + }); + expect(wrapped()).toBe(123); + }); + + it('should execute tracked callbacks', done => { + SchedulerTracking.unstable_track('some event', 0, () => { + done(); + }); + }); + + it('should return the value of a clear function', () => { + expect(SchedulerTracking.unstable_clear(() => 123)).toBe(123); + }); + + it('should execute wrapped callbacks', done => { + const wrappedCallback = SchedulerTracking.unstable_wrap(() => { + done(); + }); + + wrappedCallback(); + }); +}); diff --git a/packages/interaction-tracking/src/__tests__/InteractionTrackingSubscriptions-test.internal.js b/packages/react-scheduler/src/__tests__/TrackingSubscriptions-test.internal.js similarity index 71% rename from packages/interaction-tracking/src/__tests__/InteractionTrackingSubscriptions-test.internal.js rename to packages/react-scheduler/src/__tests__/TrackingSubscriptions-test.internal.js index 4d259858c9cb3..b346acc31d7e7 100644 --- a/packages/interaction-tracking/src/__tests__/InteractionTrackingSubscriptions-test.internal.js +++ b/packages/react-scheduler/src/__tests__/TrackingSubscriptions-test.internal.js @@ -8,9 +8,8 @@ */ 'use strict'; -describe('InteractionTracking', () => { - let InteractionTracking; - let InteractionTrackingSubscriptions; +describe('TrackingSubscriptions', () => { + let SchedulerTracking; let ReactFeatureFlags; let currentTime; @@ -34,17 +33,16 @@ describe('InteractionTracking', () => { const secondEvent = {id: 1, name: 'second', timestamp: 0}; const threadID = 123; - function loadModules({enableInteractionTracking}) { + function loadModules({enableSchedulerTracking, autoSubscribe = true}) { jest.resetModules(); jest.useFakeTimers(); currentTime = 0; ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableInteractionTracking = enableInteractionTracking; + ReactFeatureFlags.enableSchedulerTracking = enableSchedulerTracking; - InteractionTracking = require('interaction-tracking'); - InteractionTrackingSubscriptions = require('interaction-tracking/subscriptions'); + SchedulerTracking = require('react-scheduler/tracking'); throwInOnInteractionScheduledWorkCompleted = false; throwInOnInteractionTracked = false; @@ -102,22 +100,38 @@ describe('InteractionTracking', () => { onWorkStopped: jest.fn(), }; - InteractionTrackingSubscriptions.subscribe(firstSubscriber); - InteractionTrackingSubscriptions.subscribe(secondSubscriber); + if (autoSubscribe) { + SchedulerTracking.unstable_subscribe(firstSubscriber); + SchedulerTracking.unstable_subscribe(secondSubscriber); + } } describe('enabled', () => { - beforeEach(() => loadModules({enableInteractionTracking: true})); + beforeEach(() => loadModules({enableSchedulerTracking: true})); + + it('should lazily subscribe to tracking and unsubscribe again if there are no external subscribers', () => { + loadModules({enableSchedulerTracking: true, autoSubscribe: false}); + + expect(SchedulerTracking.__getSubscriberRef().current).toBe(null); + SchedulerTracking.unstable_subscribe(firstSubscriber); + expect(SchedulerTracking.__getSubscriberRef().current).toBeDefined(); + SchedulerTracking.unstable_subscribe(secondSubscriber); + expect(SchedulerTracking.__getSubscriberRef().current).toBeDefined(); + SchedulerTracking.unstable_unsubscribe(secondSubscriber); + expect(SchedulerTracking.__getSubscriberRef().current).toBeDefined(); + SchedulerTracking.unstable_unsubscribe(firstSubscriber); + expect(SchedulerTracking.__getSubscriberRef().current).toBe(null); + }); describe('error handling', () => { it('should cover onInteractionTracked/onWorkStarted within', done => { - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { const mock = jest.fn(); // It should call the callback before re-throwing throwInOnInteractionTracked = true; expect(() => - InteractionTracking.track( + SchedulerTracking.unstable_track( secondEvent.name, currentTime, mock, @@ -129,7 +143,7 @@ describe('InteractionTracking', () => { throwInOnWorkStarted = true; expect(() => - InteractionTracking.track( + SchedulerTracking.unstable_track( secondEvent.name, currentTime, mock, @@ -139,7 +153,7 @@ describe('InteractionTracking', () => { expect(mock).toHaveBeenCalledTimes(2); // It should restore the previous/outer interactions - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ firstEvent, ]); @@ -154,20 +168,26 @@ describe('InteractionTracking', () => { }); it('should cover onWorkStopped within track', done => { - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { let innerInteraction; const mock = jest.fn(() => { - innerInteraction = Array.from(InteractionTracking.getCurrent())[1]; + innerInteraction = Array.from( + SchedulerTracking.unstable_getCurrent(), + )[1]; }); throwInOnWorkStopped = true; expect(() => - InteractionTracking.track(secondEvent.name, currentTime, mock), + SchedulerTracking.unstable_track( + secondEvent.name, + currentTime, + mock, + ), ).toThrow('Expected error onWorkStopped'); throwInOnWorkStopped = false; // It should restore the previous/outer interactions - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ firstEvent, ]); @@ -182,17 +202,21 @@ describe('InteractionTracking', () => { }); it('should cover onInteractionScheduledWorkCompleted within track', done => { - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { const mock = jest.fn(); throwInOnInteractionScheduledWorkCompleted = true; expect(() => - InteractionTracking.track(secondEvent.name, currentTime, mock), + SchedulerTracking.unstable_track( + secondEvent.name, + currentTime, + mock, + ), ).toThrow('Expected error onInteractionScheduledWorkCompleted'); throwInOnInteractionScheduledWorkCompleted = false; // It should restore the previous/outer interactions - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ firstEvent, ]); @@ -210,7 +234,7 @@ describe('InteractionTracking', () => { expect(onWorkStopped).not.toHaveBeenCalled(); expect(() => { - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { throw Error('Expected error callback'); }); }).toThrow('Expected error callback'); @@ -222,12 +246,14 @@ describe('InteractionTracking', () => { }); it('should cover onWorkScheduled within wrap', done => { - InteractionTracking.track(firstEvent.name, currentTime, () => { - const interaction = Array.from(InteractionTracking.getCurrent())[0]; + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + const interaction = Array.from( + SchedulerTracking.unstable_getCurrent(), + )[0]; const beforeCount = interaction.__count; throwInOnWorkScheduled = true; - expect(() => InteractionTracking.wrap(() => {})).toThrow( + expect(() => SchedulerTracking.unstable_wrap(() => {})).toThrow( 'Expected error onWorkScheduled', ); @@ -244,9 +270,9 @@ describe('InteractionTracking', () => { it('should cover onWorkStarted within wrap', () => { const mock = jest.fn(); let interaction, wrapped; - InteractionTracking.track(firstEvent.name, currentTime, () => { - interaction = Array.from(InteractionTracking.getCurrent())[0]; - wrapped = InteractionTracking.wrap(mock); + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + interaction = Array.from(SchedulerTracking.unstable_getCurrent())[0]; + wrapped = SchedulerTracking.unstable_wrap(mock); }); expect(interaction.__count).toBe(1); @@ -264,24 +290,30 @@ describe('InteractionTracking', () => { }); it('should cover onWorkStopped within wrap', done => { - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { const outerInteraction = Array.from( - InteractionTracking.getCurrent(), + SchedulerTracking.unstable_getCurrent(), )[0]; expect(outerInteraction.__count).toBe(1); let wrapped; let innerInteraction; - InteractionTracking.track(secondEvent.name, currentTime, () => { - innerInteraction = Array.from(InteractionTracking.getCurrent())[1]; - expect(outerInteraction.__count).toBe(1); - expect(innerInteraction.__count).toBe(1); - - wrapped = InteractionTracking.wrap(jest.fn()); - expect(outerInteraction.__count).toBe(2); - expect(innerInteraction.__count).toBe(2); - }); + SchedulerTracking.unstable_track( + secondEvent.name, + currentTime, + () => { + innerInteraction = Array.from( + SchedulerTracking.unstable_getCurrent(), + )[1]; + expect(outerInteraction.__count).toBe(1); + expect(innerInteraction.__count).toBe(1); + + wrapped = SchedulerTracking.unstable_wrap(jest.fn()); + expect(outerInteraction.__count).toBe(2); + expect(innerInteraction.__count).toBe(2); + }, + ); expect(outerInteraction.__count).toBe(2); expect(innerInteraction.__count).toBe(1); @@ -291,7 +323,7 @@ describe('InteractionTracking', () => { throwInOnWorkStopped = false; // It should restore the previous interactions - expect(InteractionTracking.getCurrent()).toMatchInteractions([ + expect(SchedulerTracking.unstable_getCurrent()).toMatchInteractions([ outerInteraction, ]); @@ -311,9 +343,9 @@ describe('InteractionTracking', () => { let wrapped; let interaction; - InteractionTracking.track(firstEvent.name, currentTime, () => { - interaction = Array.from(InteractionTracking.getCurrent())[0]; - wrapped = InteractionTracking.wrap(() => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + interaction = Array.from(SchedulerTracking.unstable_getCurrent())[0]; + wrapped = SchedulerTracking.unstable_wrap(() => { throw Error('Expected error wrap'); }); }); @@ -332,9 +364,9 @@ describe('InteractionTracking', () => { it('should cover onWorkCanceled within wrap', () => { let interaction, wrapped; - InteractionTracking.track(firstEvent.name, currentTime, () => { - interaction = Array.from(InteractionTracking.getCurrent())[0]; - wrapped = InteractionTracking.wrap(jest.fn()); + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + interaction = Array.from(SchedulerTracking.unstable_getCurrent())[0]; + wrapped = SchedulerTracking.unstable_wrap(jest.fn()); }); expect(interaction.__count).toBe(1); @@ -358,7 +390,7 @@ describe('InteractionTracking', () => { expect(onInteractionTracked).not.toHaveBeenCalled(); expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); - InteractionTracking.track( + SchedulerTracking.unstable_track( firstEvent.name, currentTime, () => { @@ -374,7 +406,7 @@ describe('InteractionTracking', () => { ); expect(onWorkStopped).not.toHaveBeenCalled(); - InteractionTracking.track( + SchedulerTracking.unstable_track( secondEvent.name, currentTime, () => { @@ -426,19 +458,19 @@ describe('InteractionTracking', () => { const unwrapped = jest.fn(); let wrapped; - InteractionTracking.track(firstEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { expect(onInteractionTracked).toHaveBeenCalledTimes(1); expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( firstEvent, ); - InteractionTracking.track(secondEvent.name, currentTime, () => { + SchedulerTracking.unstable_track(secondEvent.name, currentTime, () => { expect(onInteractionTracked).toHaveBeenCalledTimes(2); expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( secondEvent, ); - wrapped = InteractionTracking.wrap(unwrapped, threadID); + wrapped = SchedulerTracking.unstable_wrap(unwrapped, threadID); expect(onWorkScheduled).toHaveBeenCalledTimes(1); expect(onWorkScheduled).toHaveBeenLastNotifiedOfWork( new Set([firstEvent, secondEvent]), @@ -478,10 +510,10 @@ describe('InteractionTracking', () => { const fnOne = jest.fn(); const fnTwo = jest.fn(); let wrappedOne, wrappedTwo; - InteractionTracking.track(firstEvent.name, currentTime, () => { - wrappedOne = InteractionTracking.wrap(fnOne, threadID); - InteractionTracking.track(secondEvent.name, currentTime, () => { - wrappedTwo = InteractionTracking.wrap(fnTwo, threadID); + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + wrappedOne = SchedulerTracking.unstable_wrap(fnOne, threadID); + SchedulerTracking.unstable_track(secondEvent.name, currentTime, () => { + wrappedTwo = SchedulerTracking.unstable_wrap(fnTwo, threadID); }); }); @@ -521,12 +553,38 @@ describe('InteractionTracking', () => { it('should not end an interaction twice if wrap is used to schedule follow up work within another wrap', () => { const fnOne = jest.fn(() => { - wrappedTwo = InteractionTracking.wrap(fnTwo, threadID); + wrappedTwo = SchedulerTracking.unstable_wrap(fnTwo, threadID); }); const fnTwo = jest.fn(); let wrappedOne, wrappedTwo; - InteractionTracking.track(firstEvent.name, currentTime, () => { - wrappedOne = InteractionTracking.wrap(fnOne, threadID); + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + wrappedOne = SchedulerTracking.unstable_wrap(fnOne, threadID); + }); + + expect(onInteractionTracked).toHaveBeenCalledTimes(1); + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + wrappedOne(); + + expect(onInteractionTracked).toHaveBeenCalledTimes(1); + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + wrappedTwo(); + + expect(onInteractionTracked).toHaveBeenCalledTimes(1); + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(firstEvent); + }); + + it('should not decrement the interaction count twice if a wrapped function is run twice', () => { + const unwrappedOne = jest.fn(); + const unwrappedTwo = jest.fn(); + let wrappedOne, wrappedTwo; + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => { + wrappedOne = SchedulerTracking.unstable_wrap(unwrappedOne, threadID); + wrappedTwo = SchedulerTracking.unstable_wrap(unwrappedTwo, threadID); }); expect(onInteractionTracked).toHaveBeenCalledTimes(1); @@ -534,6 +592,13 @@ describe('InteractionTracking', () => { wrappedOne(); + expect(unwrappedOne).toHaveBeenCalledTimes(1); + expect(onInteractionTracked).toHaveBeenCalledTimes(1); + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + wrappedOne(); + + expect(unwrappedOne).toHaveBeenCalledTimes(2); expect(onInteractionTracked).toHaveBeenCalledTimes(1); expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); @@ -547,15 +612,15 @@ describe('InteractionTracking', () => { }); it('should unsubscribe', () => { - InteractionTrackingSubscriptions.unsubscribe(firstSubscriber); - InteractionTracking.track(firstEvent.name, currentTime, () => {}); + SchedulerTracking.unstable_unsubscribe(firstSubscriber); + SchedulerTracking.unstable_track(firstEvent.name, currentTime, () => {}); expect(onInteractionTracked).not.toHaveBeenCalled(); }); }); describe('disabled', () => { - beforeEach(() => loadModules({enableInteractionTracking: false})); + beforeEach(() => loadModules({enableSchedulerTracking: false})); // TODO }); diff --git a/packages/interaction-tracking/subscriptions.js b/packages/react-scheduler/tracking.js similarity index 73% rename from packages/interaction-tracking/subscriptions.js rename to packages/react-scheduler/tracking.js index 91bf6e66b9791..314a84cbf6970 100644 --- a/packages/interaction-tracking/subscriptions.js +++ b/packages/react-scheduler/tracking.js @@ -9,4 +9,5 @@ 'use strict'; -export * from './src/InteractionTrackingSubscriptions'; +export * from './src/Tracking'; +export * from './src/TrackingSubscriptions'; diff --git a/packages/react-test-renderer/package.json b/packages/react-test-renderer/package.json index 5ff1da8bf394e..51cc285a5ed2e 100644 --- a/packages/react-test-renderer/package.json +++ b/packages/react-test-renderer/package.json @@ -17,7 +17,8 @@ "dependencies": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.4.3-alpha.0" + "react-is": "^16.4.3-alpha.0", + "react-scheduler": "^0.1.0-alpha-1" }, "peerDependencies": { "react": "^16.0.0" diff --git a/packages/react/package.json b/packages/react/package.json index 6c9ad1ad537f2..6597609f0970f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,7 +23,8 @@ "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "prop-types": "^15.6.2", + "react-scheduler": "^0.1.0-alpha-1" }, "browserify": { "transform": [ diff --git a/packages/react/src/ReactSharedInternals.js b/packages/react/src/ReactSharedInternals.js index 8b179aff8422c..a31f2fe5601b7 100644 --- a/packages/react/src/ReactSharedInternals.js +++ b/packages/react/src/ReactSharedInternals.js @@ -6,6 +6,22 @@ */ import assign from 'object-assign'; +import { + unstable_cancelScheduledWork, + unstable_now, + unstable_scheduleWork, +} from 'react-scheduler'; +import { + __getInteractionsRef, + __getSubscriberRef, + unstable_clear, + unstable_getCurrent, + unstable_getThreadID, + unstable_subscribe, + unstable_track, + unstable_unsubscribe, + unstable_wrap, +} from 'react-scheduler/tracking'; import ReactCurrentOwner from './ReactCurrentOwner'; import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; @@ -15,6 +31,32 @@ const ReactSharedInternals = { assign, }; +if (__UMD__) { + // Re-export the react-scheduler API(s) for UMD bundles. + // This avoids introducing a dependency on a new UMD global in a minor update, + // Since that would be a breaking change (e.g. for all existing CodeSandboxes). + // This re-export is only required for UMD bundles; + // CJS bundles use the shared NPM package. + Object.assign(ReactSharedInternals, { + Scheduler: { + unstable_cancelScheduledWork, + unstable_now, + unstable_scheduleWork, + }, + SchedulerTracking: { + __getInteractionsRef, + __getSubscriberRef, + unstable_clear, + unstable_getCurrent, + unstable_getThreadID, + unstable_subscribe, + unstable_track, + unstable_unsubscribe, + unstable_wrap, + }, + }); +} + if (__DEV__) { Object.assign(ReactSharedInternals, { // These should not be included in production. diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index 5ccc00f31a7f9..749c6f48209e9 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -10,20 +10,19 @@ 'use strict'; -let InteractionTracking; -let InteractionTrackingSubscriptions; let React; let ReactFeatureFlags; let ReactNoop; let ReactTestRenderer; let advanceTimeBy; +let SchedulerTracking; let mockNow; let AdvanceTime; function loadModules({ enableProfilerTimer = true, enableSuspense = false, - enableInteractionTracking = true, + enableSchedulerTracking = true, replayFailedUnitOfWorkWithInvokeGuardedCallback = false, useNoopRenderer = false, } = {}) { @@ -38,13 +37,12 @@ function loadModules({ ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false; ReactFeatureFlags.enableProfilerTimer = enableProfilerTimer; ReactFeatureFlags.enableGetDerivedStateFromCatch = true; - ReactFeatureFlags.enableInteractionTracking = enableInteractionTracking; + ReactFeatureFlags.enableSchedulerTracking = enableSchedulerTracking; ReactFeatureFlags.enableSuspense = enableSuspense; ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = replayFailedUnitOfWorkWithInvokeGuardedCallback; - InteractionTracking = require('interaction-tracking'); - InteractionTrackingSubscriptions = require('interaction-tracking/subscriptions'); React = require('react'); + SchedulerTracking = require('react-scheduler/tracking'); if (useNoopRenderer) { ReactNoop = require('react-noop-renderer'); @@ -84,17 +82,17 @@ const mockDevToolsForTest = () => { describe('Profiler', () => { describe('works in profiling and non-profiling bundles', () => { - [true, false].forEach(enableInteractionTracking => { + [true, false].forEach(enableSchedulerTracking => { [true, false].forEach(enableProfilerTimer => { - describe(`enableInteractionTracking:${ - enableInteractionTracking ? 'enabled' : 'disabled' + describe(`enableSchedulerTracking:${ + enableSchedulerTracking ? 'enabled' : 'disabled' } enableProfilerTimer:${ enableProfilerTimer ? 'enabled' : 'disabled' }`, () => { beforeEach(() => { jest.resetModules(); - loadModules({enableInteractionTracking, enableProfilerTimer}); + loadModules({enableSchedulerTracking, enableProfilerTimer}); }); // This will throw in production too, @@ -167,12 +165,12 @@ describe('Profiler', () => { }); }); - [true, false].forEach(enableInteractionTracking => { + [true, false].forEach(enableSchedulerTracking => { describe('onRender callback', () => { beforeEach(() => { jest.resetModules(); - loadModules({enableInteractionTracking}); + loadModules({enableSchedulerTracking}); }); it('is not invoked until the commit phase', () => { @@ -233,7 +231,7 @@ describe('Profiler', () => { let [call] = callback.mock.calls; - expect(call).toHaveLength(enableInteractionTracking ? 7 : 6); + expect(call).toHaveLength(enableSchedulerTracking ? 7 : 6); expect(call[0]).toBe('test'); expect(call[1]).toBe('mount'); expect(call[2]).toBe(10); // actual time @@ -241,7 +239,7 @@ describe('Profiler', () => { expect(call[4]).toBe(5); // start time expect(call[5]).toBe(15); // commit time expect(call[6]).toEqual( - enableInteractionTracking ? new Set() : undefined, + enableSchedulerTracking ? new Set() : undefined, ); // interaction events callback.mockReset(); @@ -258,7 +256,7 @@ describe('Profiler', () => { [call] = callback.mock.calls; - expect(call).toHaveLength(enableInteractionTracking ? 7 : 6); + expect(call).toHaveLength(enableSchedulerTracking ? 7 : 6); expect(call[0]).toBe('test'); expect(call[1]).toBe('update'); expect(call[2]).toBe(10); // actual time @@ -266,7 +264,7 @@ describe('Profiler', () => { expect(call[4]).toBe(35); // start time expect(call[5]).toBe(45); // commit time expect(call[6]).toEqual( - enableInteractionTracking ? new Set() : undefined, + enableSchedulerTracking ? new Set() : undefined, ); // interaction events callback.mockReset(); @@ -283,7 +281,7 @@ describe('Profiler', () => { [call] = callback.mock.calls; - expect(call).toHaveLength(enableInteractionTracking ? 7 : 6); + expect(call).toHaveLength(enableSchedulerTracking ? 7 : 6); expect(call[0]).toBe('test'); expect(call[1]).toBe('update'); expect(call[2]).toBe(4); // actual time @@ -291,7 +289,7 @@ describe('Profiler', () => { expect(call[4]).toBe(65); // start time expect(call[5]).toBe(69); // commit time expect(call[6]).toEqual( - enableInteractionTracking ? new Set() : undefined, + enableSchedulerTracking ? new Set() : undefined, ); // interaction events }); @@ -1176,7 +1174,7 @@ describe('Profiler', () => { jest.resetModules(); loadModules({ - enableInteractionTracking: true, + enableSchedulerTracking: true, }); throwInOnInteractionScheduledWorkCompleted = false; @@ -1208,7 +1206,7 @@ describe('Profiler', () => { }); // Verify interaction subscriber methods are called as expected. - InteractionTrackingSubscriptions.subscribe({ + SchedulerTracking.unstable_subscribe({ onInteractionScheduledWorkCompleted, onInteractionTracked, onWorkCanceled, @@ -1230,7 +1228,7 @@ describe('Profiler', () => { // Errors that happen inside of a subscriber should throw, throwInOnWorkScheduled = true; expect(() => { - InteractionTracking.track('event', mockNow(), () => { + SchedulerTracking.unstable_track('event', mockNow(), () => { renderer = ReactTestRenderer.create(fail, { unstable_isAsync: true, }); @@ -1256,7 +1254,7 @@ describe('Profiler', () => { } let renderer; - InteractionTracking.track('event', mockNow(), () => { + SchedulerTracking.unstable_track('event', mockNow(), () => { renderer = ReactTestRenderer.create(text, { unstable_isAsync: true, }); @@ -1285,7 +1283,7 @@ describe('Profiler', () => { } let renderer; - InteractionTracking.track('event', mockNow(), () => { + SchedulerTracking.unstable_track('event', mockNow(), () => { renderer = ReactTestRenderer.create(text, { unstable_isAsync: true, }); @@ -1327,8 +1325,8 @@ describe('Profiler', () => { }; let renderer; - InteractionTracking.track(eventOne.name, mockNow(), () => { - InteractionTracking.track(eventTwo.name, mockNow(), () => { + SchedulerTracking.unstable_track(eventOne.name, mockNow(), () => { + SchedulerTracking.unstable_track(eventTwo.name, mockNow(), () => { renderer = ReactTestRenderer.create(text, { unstable_isAsync: true, }); @@ -1382,16 +1380,20 @@ describe('Profiler', () => { const onRender = jest.fn(); let renderer; - InteractionTracking.track(interactionCreation.name, mockNow(), () => { - renderer = ReactTestRenderer.create( - - - , - { - unstable_isAsync: true, - }, - ); - }); + SchedulerTracking.unstable_track( + interactionCreation.name, + mockNow(), + () => { + renderer = ReactTestRenderer.create( + + + , + { + unstable_isAsync: true, + }, + ); + }, + ); expect(onInteractionTracked).toHaveBeenCalledTimes(1); expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( @@ -1399,7 +1401,7 @@ describe('Profiler', () => { ); expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); - // The interaction-tracking package will notify of work started for the default thread, + // The react-scheduler/tracking package will notify of work started for the default thread, // But React shouldn't notify until it's been flushed. expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0); expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); @@ -1419,7 +1421,7 @@ describe('Profiler', () => { let call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[5]).toEqual(mockNow()); - if (ReactFeatureFlags.enableInteractionTracking) { + if (ReactFeatureFlags.enableSchedulerTracking) { expect(call[6]).toMatchInteractions([interactionCreation]); } @@ -1451,13 +1453,13 @@ describe('Profiler', () => { name: 'initial event', timestamp: mockNow(), }; - InteractionTracking.track(interactionOne.name, mockNow(), () => { + SchedulerTracking.unstable_track(interactionOne.name, mockNow(), () => { instance.setState({count: 1}); // Update state again to verify our tracked interaction isn't registered twice instance.setState({count: 2}); - // The interaction-tracking package will notify of work started for the default thread, + // The react-scheduler/tracking package will notify of work started for the default thread, // But React shouldn't notify until it's been flushed. expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0); expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); @@ -1491,7 +1493,7 @@ describe('Profiler', () => { call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[5]).toEqual(mockNow()); - if (ReactFeatureFlags.enableInteractionTracking) { + if (ReactFeatureFlags.enableSchedulerTracking) { expect(call[6]).toMatchInteractions([interactionOne]); } @@ -1524,7 +1526,7 @@ describe('Profiler', () => { call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[5]).toEqual(mockNow()); - if (ReactFeatureFlags.enableInteractionTracking) { + if (ReactFeatureFlags.enableSchedulerTracking) { expect(call[6]).toMatchInteractions([]); } @@ -1546,7 +1548,7 @@ describe('Profiler', () => { name: 'root update event', timestamp: mockNow(), }; - InteractionTracking.track(interactionTwo.name, mockNow(), () => { + SchedulerTracking.unstable_track(interactionTwo.name, mockNow(), () => { renderer.update( @@ -1560,7 +1562,7 @@ describe('Profiler', () => { ); expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2); - // The interaction-tracking package will notify of work started for the default thread, + // The react-scheduler/tracking package will notify of work started for the default thread, // But React shouldn't notify until it's been flushed. expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0); expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); @@ -1580,7 +1582,7 @@ describe('Profiler', () => { call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[5]).toEqual(mockNow()); - if (ReactFeatureFlags.enableInteractionTracking) { + if (ReactFeatureFlags.enableSchedulerTracking) { expect(call[6]).toMatchInteractions([interactionTwo]); } @@ -1647,109 +1649,119 @@ describe('Profiler', () => { timestamp: mockNow(), }; - InteractionTracking.track(interactionLowPri.name, mockNow(), () => { - // Render a partially update, but don't finish. - first.setState({count: 1}); - - expect(onWorkScheduled).toHaveBeenCalled(); - expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([ - interactionLowPri, - ]); - - expect(renderer).toFlushThrough(['FirstComponent']); - expect(onRender).not.toHaveBeenCalled(); - - expect(onInteractionTracked).toHaveBeenCalledTimes(1); - expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( - interactionLowPri, - ); - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1); - expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions( - [interactionLowPri], - ); - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); + SchedulerTracking.unstable_track( + interactionLowPri.name, + mockNow(), + () => { + // Render a partially update, but don't finish. + first.setState({count: 1}); - advanceTimeBy(100); + expect(onWorkScheduled).toHaveBeenCalled(); + expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([ + interactionLowPri, + ]); - const interactionHighPri = { - id: 1, - name: 'highPri', - timestamp: mockNow(), - }; + expect(renderer).toFlushThrough(['FirstComponent']); + expect(onRender).not.toHaveBeenCalled(); - // Interrupt with higher priority work. - // This simulates a total of 37ms of actual render time. - renderer.unstable_flushSync(() => { - InteractionTracking.track(interactionHighPri.name, mockNow(), () => { - second.setState({count: 1}); + expect(onInteractionTracked).toHaveBeenCalledTimes(1); + expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( + interactionLowPri, + ); + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1); + expect( + getWorkForReactThreads(onWorkStarted)[0][0], + ).toMatchInteractions([interactionLowPri]); + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); + + advanceTimeBy(100); + + const interactionHighPri = { + id: 1, + name: 'highPri', + timestamp: mockNow(), + }; - expect(onInteractionTracked).toHaveBeenCalledTimes(2); - expect(onInteractionTracked).toHaveBeenLastNotifiedOfInteraction( - interactionHighPri, + // Interrupt with higher priority work. + // This simulates a total of 37ms of actual render time. + renderer.unstable_flushSync(() => { + SchedulerTracking.unstable_track( + interactionHighPri.name, + mockNow(), + () => { + second.setState({count: 1}); + + expect(onInteractionTracked).toHaveBeenCalledTimes(2); + expect( + onInteractionTracked, + ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri); + expect( + onInteractionScheduledWorkCompleted, + ).not.toHaveBeenCalled(); + + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1); + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); + }, ); - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); - - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1); - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); }); - }); - expect(ReactTestRenderer).toClearYields(['SecondComponent']); - - expect(onInteractionTracked).toHaveBeenCalledTimes(2); - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); - expect( - onInteractionScheduledWorkCompleted, - ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri); - - // Verify the high priority update was associated with the high priority event. - expect(onRender).toHaveBeenCalledTimes(1); - let call = onRender.mock.calls[0]; - expect(call[0]).toEqual('test'); - expect(call[5]).toEqual(mockNow()); - expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking - ? [interactionLowPri, interactionHighPri] - : [], - ); + expect(ReactTestRenderer).toClearYields(['SecondComponent']); + + expect(onInteractionTracked).toHaveBeenCalledTimes(2); + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri); + + // Verify the high priority update was associated with the high priority event. + expect(onRender).toHaveBeenCalledTimes(1); + let call = onRender.mock.calls[0]; + expect(call[0]).toEqual('test'); + expect(call[5]).toEqual(mockNow()); + expect(call[6]).toMatchInteractions( + ReactFeatureFlags.enableSchedulerTracking + ? [interactionLowPri, interactionHighPri] + : [], + ); - onRender.mockClear(); + onRender.mockClear(); - advanceTimeBy(100); + advanceTimeBy(100); - // Resume the original low priority update, with rebased state. - // Verify the low priority update was retained. - renderer.unstable_flushAll(['FirstComponent']); - expect(onRender).toHaveBeenCalledTimes(1); - call = onRender.mock.calls[0]; - expect(call[0]).toEqual('test'); - expect(call[5]).toEqual(mockNow()); - expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking - ? [interactionLowPri] - : [], - ); - - expect(onInteractionTracked).toHaveBeenCalledTimes(2); - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + // Resume the original low priority update, with rebased state. + // Verify the low priority update was retained. + renderer.unstable_flushAll(['FirstComponent']); + expect(onRender).toHaveBeenCalledTimes(1); + call = onRender.mock.calls[0]; + expect(call[0]).toEqual('test'); + expect(call[5]).toEqual(mockNow()); + expect(call[6]).toMatchInteractions( + ReactFeatureFlags.enableSchedulerTracking + ? [interactionLowPri] + : [], + ); - // Work might be started multiple times before being completed. - // This is okay; it's part of the interaction-tracking subscriber contract. - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3); - expect(getWorkForReactThreads(onWorkStarted)[1][0]).toMatchInteractions( - [interactionLowPri, interactionHighPri], - ); - expect(getWorkForReactThreads(onWorkStarted)[2][0]).toMatchInteractions( - [interactionLowPri], - ); - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2); - expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions( - [interactionLowPri, interactionHighPri], - ); - expect(getWorkForReactThreads(onWorkStopped)[1][0]).toMatchInteractions( - [interactionLowPri], - ); - }); + expect(onInteractionTracked).toHaveBeenCalledTimes(2); + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + + // Work might be started multiple times before being completed. + // This is okay; it's part of the react-scheduler/tracking contract. + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3); + expect( + getWorkForReactThreads(onWorkStarted)[1][0], + ).toMatchInteractions([interactionLowPri, interactionHighPri]); + expect( + getWorkForReactThreads(onWorkStarted)[2][0], + ).toMatchInteractions([interactionLowPri]); + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2); + expect( + getWorkForReactThreads(onWorkStopped)[0][0], + ).toMatchInteractions([interactionLowPri, interactionHighPri]); + expect( + getWorkForReactThreads(onWorkStopped)[1][0], + ).toMatchInteractions([interactionLowPri]); + }, + ); expect(onInteractionTracked).toHaveBeenCalledTimes(2); expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2); @@ -1793,7 +1805,7 @@ describe('Profiler', () => { const onRender = jest.fn(); let firstCommitTime = mockNow(); let renderer; - InteractionTracking.track(interactionOne.name, mockNow(), () => { + SchedulerTracking.unstable_track(interactionOne.name, mockNow(), () => { renderer = ReactTestRenderer.create( @@ -1837,13 +1849,13 @@ describe('Profiler', () => { expect(call[0]).toEqual('test'); expect(call[5]).toEqual(firstCommitTime); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionOne] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionOne] : [], ); call = onRender.mock.calls[1]; expect(call[0]).toEqual('test'); expect(call[5]).toEqual(mockNow()); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionOne] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionOne] : [], ); onRender.mockClear(); @@ -1855,7 +1867,7 @@ describe('Profiler', () => { }; // Cause an tracked, async update - InteractionTracking.track(interactionTwo.name, mockNow(), () => { + SchedulerTracking.unstable_track(interactionTwo.name, mockNow(), () => { instance.setState({count: 2}); }); expect(onRender).not.toHaveBeenCalled(); @@ -1900,13 +1912,13 @@ describe('Profiler', () => { expect(call[0]).toEqual('test'); expect(call[5]).toEqual(firstCommitTime); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionTwo] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionTwo] : [], ); call = onRender.mock.calls[1]; expect(call[0]).toEqual('test'); expect(call[5]).toEqual(mockNow()); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionTwo] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionTwo] : [], ); onRender.mockClear(); @@ -1921,7 +1933,7 @@ describe('Profiler', () => { function callback() { instance.setState({count: 6}); } - InteractionTracking.track(interactionThree.name, mockNow(), () => { + SchedulerTracking.unstable_track(interactionThree.name, mockNow(), () => { instance.setState({count: 5}, callback); }); expect(onRender).not.toHaveBeenCalled(); @@ -1965,13 +1977,13 @@ describe('Profiler', () => { expect(call[0]).toEqual('test'); expect(call[5]).toEqual(firstCommitTime); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionThree] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionThree] : [], ); call = onRender.mock.calls[1]; expect(call[0]).toEqual('test'); expect(call[5]).toEqual(mockNow()); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interactionThree] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interactionThree] : [], ); }); @@ -2014,7 +2026,7 @@ describe('Profiler', () => { timestamp: mockNow(), }; - InteractionTracking.track(interaction.name, mockNow(), () => { + SchedulerTracking.unstable_track(interaction.name, mockNow(), () => { parentInstance.setState({count: 1}); }); @@ -2032,7 +2044,7 @@ describe('Profiler', () => { let call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interaction] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interaction] : [], ); expect(onInteractionTracked).toHaveBeenCalledTimes(1); @@ -2056,11 +2068,11 @@ describe('Profiler', () => { loadModules({ useNoopRenderer: true, enableSuspense: true, - enableInteractionTracking: true, + enableSchedulerTracking: true, }); // Re-register since we've reloaded modules - InteractionTrackingSubscriptions.subscribe({ + SchedulerTracking.unstable_subscribe({ onInteractionScheduledWorkCompleted, onInteractionTracked, onWorkCanceled, @@ -2127,7 +2139,7 @@ describe('Profiler', () => { }; const onRender = jest.fn(); - InteractionTracking.track(interaction.name, mockNow(), () => { + SchedulerTracking.unstable_track(interaction.name, mockNow(), () => { ReactNoop.render( }> @@ -2172,7 +2184,7 @@ describe('Profiler', () => { let call = onRender.mock.calls[0]; expect(call[0]).toEqual('test-profiler'); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interaction] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interaction] : [], ); expect(onInteractionTracked).toHaveBeenCalledTimes(1); @@ -2187,7 +2199,7 @@ describe('Profiler', () => { call = onRender.mock.calls[1]; expect(call[0]).toEqual('test-profiler'); expect(call[6]).toMatchInteractions( - ReactFeatureFlags.enableInteractionTracking ? [interaction] : [], + ReactFeatureFlags.enableSchedulerTracking ? [interaction] : [], ); expect(onInteractionTracked).toHaveBeenCalledTimes(1); diff --git a/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js b/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js index 201c9a1803c7f..08e2e87bd2526 100644 --- a/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js +++ b/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js @@ -14,7 +14,7 @@ describe('ReactProfiler DevTools integration', () => { let React; let ReactFeatureFlags; let ReactTestRenderer; - let InteractionTracking; + let SchedulerTracking; let AdvanceTime; let advanceTimeBy; let hook; @@ -38,10 +38,10 @@ describe('ReactProfiler DevTools integration', () => { ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.enableProfilerTimer = true; - ReactFeatureFlags.enableInteractionTracking = true; + ReactFeatureFlags.enableSchedulerTracking = true; + SchedulerTracking = require('react-scheduler/tracking'); React = require('react'); ReactTestRenderer = require('react-test-renderer'); - InteractionTracking = require('interaction-tracking'); ReactTestRenderer.unstable_setNowImplementation(mockNow); advanceTimeBy = amount => { @@ -180,7 +180,7 @@ describe('ReactProfiler DevTools integration', () => { const eventTime = mockNow(); // Render with an interaction - InteractionTracking.track('some event', eventTime, () => { + SchedulerTracking.unstable_track('some event', eventTime, () => { rendered.update(
); }); diff --git a/packages/react/src/__tests__/__snapshots__/ReactProfiler-test.internal.js.snap b/packages/react/src/__tests__/__snapshots__/ReactProfiler-test.internal.js.snap index 06e00d35930c4..18f6f0ac516fa 100644 --- a/packages/react/src/__tests__/__snapshots__/ReactProfiler-test.internal.js.snap +++ b/packages/react/src/__tests__/__snapshots__/ReactProfiler-test.internal.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:disabled should render children 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:disabled should render children 1`] = `
outside span @@ -14,11 +14,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableInteraction
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `
`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:disabled should support nested Profilers 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:disabled should support nested Profilers 1`] = ` Array [
outer functional component @@ -32,7 +32,7 @@ Array [ ] `; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:enabled should render children 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:enabled should render children 1`] = `
outside span @@ -46,11 +46,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableInteraction
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `
`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:disabled enableProfilerTimer:enabled should support nested Profilers 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:disabled enableProfilerTimer:enabled should support nested Profilers 1`] = ` Array [
outer functional component @@ -64,7 +64,7 @@ Array [ ] `; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:disabled should render children 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:disabled should render children 1`] = `
outside span @@ -78,11 +78,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableInteraction
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 1`] = `null`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `
`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:disabled should support an empty Profiler (with no children) 2`] = `
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:disabled should support nested Profilers 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:disabled should support nested Profilers 1`] = ` Array [
outer functional component @@ -96,7 +96,7 @@ Array [ ] `; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:enabled should render children 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:enabled should render children 1`] = `
outside span @@ -110,11 +110,11 @@ exports[`Profiler works in profiling and non-profiling bundles enableInteraction
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 1`] = `null`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `
`; +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:enabled should support an empty Profiler (with no children) 2`] = `
`; -exports[`Profiler works in profiling and non-profiling bundles enableInteractionTracking:enabled enableProfilerTimer:enabled should support nested Profilers 1`] = ` +exports[`Profiler works in profiling and non-profiling bundles enableSchedulerTracking:enabled enableProfilerTimer:enabled should support nested Profilers 1`] = ` Array [
outer functional component diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index aa9fc017fca70..c0e53946eaa01 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -40,7 +40,7 @@ export const warnAboutLegacyContextAPI = false; export const enableProfilerTimer = __PROFILE__; // Track which interactions trigger each commit. -export const enableInteractionTracking = __PROFILE__; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/ReactScheduler.js b/packages/shared/ReactScheduler.js deleted file mode 100644 index c65d28ecfaf7c..0000000000000 --- a/packages/shared/ReactScheduler.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -'use strict'; -import { - now, - scheduleWork, - cancelScheduledWork, -} from 'react-scheduler/src/ReactScheduler'; - -export {now, scheduleWork, cancelScheduledWork}; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js index 49e6d6397f1a0..634a9236aacd9 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js @@ -21,7 +21,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = __DEV__; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js b/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js index 907efd059e6a3..210e40c449f9a 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js @@ -21,7 +21,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 2915c31bfef46..f86dd8c5cfc35 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -26,7 +26,7 @@ export const { export const enableUserTimingAPI = __DEV__; export const warnAboutLegacyContextAPI = __DEV__; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index dfcf41a618d29..8097e29d959d8 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -21,7 +21,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.persistent.js b/packages/shared/forks/ReactFeatureFlags.persistent.js index ada769a931b65..0d78dacdf2e40 100644 --- a/packages/shared/forks/ReactFeatureFlags.persistent.js +++ b/packages/shared/forks/ReactFeatureFlags.persistent.js @@ -21,7 +21,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = __PROFILE__; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 773a1d6b2d8d3..280359f9997af 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -21,7 +21,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 49bbec5025542..4fa93944f3066 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -21,7 +21,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; -export const enableInteractionTracking = false; +export const enableSchedulerTracking = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 37b88db984913..5baa9cdff0966 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -32,7 +32,7 @@ export const warnAboutLegacyContextAPI = __DEV__; export let enableUserTimingAPI = __DEV__; export const enableProfilerTimer = __PROFILE__; -export const enableInteractionTracking = __PROFILE__; +export const enableSchedulerTracking = __PROFILE__; let refCount = 0; export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactScheduler.umd.js b/packages/shared/forks/ReactScheduler.umd.js new file mode 100644 index 0000000000000..81adf456e03e3 --- /dev/null +++ b/packages/shared/forks/ReactScheduler.umd.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import React from 'react'; + +const ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +const { + unstable_cancelScheduledWork, + unstable_now, + unstable_scheduleWork, +} = ReactInternals.Scheduler; + +export {unstable_cancelScheduledWork, unstable_now, unstable_scheduleWork}; diff --git a/packages/shared/forks/ReactScheduler.www.js b/packages/shared/forks/ReactScheduler.www.js index 4f76345a1c20f..0df242dfb1e9b 100644 --- a/packages/shared/forks/ReactScheduler.www.js +++ b/packages/shared/forks/ReactScheduler.www.js @@ -6,6 +6,10 @@ */ 'use strict'; -const {now, scheduleWork, cancelScheduledWork} = require('customSchedule'); +const { + unstable_now, + unstable_scheduleWork, + unstable_cancelScheduledWork, +} = require('customSchedule'); -export {now, scheduleWork, cancelScheduledWork}; +export {unstable_now, unstable_scheduleWork, unstable_cancelScheduledWork}; diff --git a/packages/shared/forks/ReactSchedulerTracking.umd.js b/packages/shared/forks/ReactSchedulerTracking.umd.js new file mode 100644 index 0000000000000..5b66ea93e8df9 --- /dev/null +++ b/packages/shared/forks/ReactSchedulerTracking.umd.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import React from 'react'; + +const ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +const { + __getInteractionsRef, + __getSubscriberRef, + unstable_clear, + unstable_getCurrent, + unstable_getThreadID, + unstable_subscribe, + unstable_track, + unstable_unsubscribe, + unstable_wrap, +} = ReactInternals.SchedulerTracking; + +export { + __getInteractionsRef, + __getSubscriberRef, + unstable_clear, + unstable_getCurrent, + unstable_getThreadID, + unstable_subscribe, + unstable_track, + unstable_unsubscribe, + unstable_wrap, +}; diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 8cb7e7192dca6..ab265b568133c 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -10,6 +10,7 @@ /* eslint-disable */ declare var __PROFILE__: boolean; +declare var __UMD__: boolean; declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) diff --git a/scripts/jest/setupEnvironment.js b/scripts/jest/setupEnvironment.js index 009ad89453ca1..d216e5f6ceaa3 100644 --- a/scripts/jest/setupEnvironment.js +++ b/scripts/jest/setupEnvironment.js @@ -6,6 +6,7 @@ if (NODE_ENV !== 'development' && NODE_ENV !== 'production') { } global.__DEV__ = NODE_ENV === 'development'; global.__PROFILE__ = NODE_ENV === 'development'; +global.__UMD__ = false; if (typeof window !== 'undefined') { global.requestAnimationFrame = function(callback) { diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index fa1c8f90c6630..c0ddb6e2dbee2 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -18,6 +18,7 @@ const Stats = require('./stats'); const Sync = require('./sync'); const sizes = require('./plugins/sizes-plugin'); const useForks = require('./plugins/use-forks-plugin'); +const stripUnusedImports = require('./plugins/strip-unused-imports'); const extractErrorCodes = require('../error-codes/extract-errors'); const Packaging = require('./packaging'); const {asyncCopyTo, asyncRimRaf} = require('./utils'); @@ -272,7 +273,8 @@ function getPlugins( bundleType, globalName, moduleType, - modulesToStub + modulesToStub, + pureExternalModules ) { const findAndRecordErrorCodes = extractErrorCodes(errorCodeOpts); const forks = Modules.getForks(bundleType, entry, moduleType); @@ -323,6 +325,7 @@ function getPlugins( replace({ __DEV__: isProduction ? 'false' : 'true', __PROFILE__: isProfiling || !isProduction ? 'true' : 'false', + __UMD__: isUMDBundle ? 'true' : 'false', 'process.env.NODE_ENV': isProduction ? "'production'" : "'development'", }), // We still need CommonJS for external deps like object-assign. @@ -349,6 +352,9 @@ function getPlugins( renaming: !shouldStayReadable, }) ), + // HACK to work around the fact that Rollup isn't removing unused, pure-module imports. + // Note that this plugin must be called after closure applies DCE. + isProduction && stripUnusedImports(pureExternalModules), // Add the whitespace back if necessary. shouldStayReadable && prettier({parser: 'babylon'}), // License and haste headers, top-level `if` blocks. @@ -468,7 +474,8 @@ async function createBundle(bundle, bundleType) { bundleType, bundle.global, bundle.moduleType, - bundle.modulesToStub + bundle.modulesToStub, + pureExternalModules ), // We can't use getters in www. legacy: diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index a656c4be24396..c35bf046f2e2b 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -60,8 +60,10 @@ const bundles = [ UMD_PROD, NODE_DEV, NODE_PROD, + NODE_PROFILING, FB_WWW_DEV, FB_WWW_PROD, + FB_WWW_PROFILING, ], moduleType: ISOMORPHIC, entry: 'react', @@ -85,7 +87,7 @@ const bundles = [ moduleType: RENDERER, entry: 'react-dom', global: 'ReactDOM', - externals: ['interaction-tracking', 'react'], + externals: ['react'], }, //******* Test Utils *******/ @@ -129,7 +131,7 @@ const bundles = [ moduleType: NON_FIBER_RENDERER, entry: 'react-dom/server.browser', global: 'ReactDOMServer', - externals: ['interaction-tracking', 'react'], + externals: ['react'], }, { @@ -137,7 +139,7 @@ const bundles = [ bundleTypes: [NODE_DEV, NODE_PROD], moduleType: NON_FIBER_RENDERER, entry: 'react-dom/server.node', - externals: ['interaction-tracking', 'react', 'stream'], + externals: ['react', 'stream'], }, /******* React ART *******/ @@ -179,7 +181,6 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', - 'interaction-tracking', 'ReactNativeViewConfigRegistry', ], }, @@ -200,7 +201,6 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', - 'interaction-tracking', 'ReactNativeViewConfigRegistry', ], }, @@ -223,7 +223,6 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', - 'interaction-tracking', 'ReactNativeViewConfigRegistry', ], }, @@ -245,7 +244,6 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', - 'interaction-tracking', 'ReactNativeViewConfigRegistry', ], }, @@ -257,7 +255,7 @@ const bundles = [ moduleType: RENDERER, entry: 'react-test-renderer', global: 'ReactTestRenderer', - externals: ['interaction-tracking', 'react'], + externals: ['react'], }, { @@ -322,7 +320,7 @@ const bundles = [ moduleType: RECONCILER, entry: 'react-reconciler', global: 'ReactReconciler', - externals: ['interaction-tracking', 'react'], + externals: ['react'], }, /******* React Persistent Reconciler *******/ @@ -332,7 +330,7 @@ const bundles = [ moduleType: RECONCILER, entry: 'react-reconciler/persistent', global: 'ReactPersistentReconciler', - externals: ['interaction-tracking', 'react'], + externals: ['react'], }, /******* Reflection *******/ @@ -385,38 +383,28 @@ const bundles = [ /******* React Scheduler (experimental) *******/ { label: 'react-scheduler', - bundleTypes: [ - UMD_DEV, - UMD_PROD, - NODE_DEV, - NODE_PROD, - FB_WWW_DEV, - FB_WWW_PROD, - ], + bundleTypes: [NODE_DEV, NODE_PROD, FB_WWW_DEV, FB_WWW_PROD], moduleType: ISOMORPHIC, entry: 'react-scheduler', global: 'ReactScheduler', externals: [], }, - /******* interaction-tracking (experimental) *******/ { - label: 'interaction-tracking', - bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD], + label: 'react-scheduler-tracking', + bundleTypes: [ + FB_WWW_DEV, + FB_WWW_PROD, + FB_WWW_PROFILING, + NODE_DEV, + NODE_PROD, + NODE_PROFILING, + ], moduleType: ISOMORPHIC, - entry: 'interaction-tracking', - global: 'InteractionTracking', + entry: 'react-scheduler/tracking', + global: 'ReactSchedulerTracking', externals: [], }, - - { - label: 'interaction-tracking-subscriptions', - bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD], - moduleType: ISOMORPHIC, - entry: 'interaction-tracking/subscriptions', - global: 'InteractionTrackingSubscriptions', - externals: ['interaction-tracking'], - }, ]; // Based on deep-freeze by substack (public domain) diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index ba4a7fe3db282..5ebd3cb4b1c63 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -114,13 +114,42 @@ const forks = Object.freeze({ return null; }, - 'shared/ReactScheduler': (bundleType, entry) => { + 'react-scheduler': (bundleType, entry, dependencies) => { switch (bundleType) { case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: return 'shared/forks/ReactScheduler.www.js'; + case UMD_DEV: + case UMD_PROD: + if (dependencies.indexOf('react') === -1) { + // It's only safe to use this fork for modules that depend on React, + // because they read the re-exported API from the SECRET_INTERNALS object. + return null; + } + // Optimization: for UMDs, use the API that is already a part of the React + // package instead of requiring it to be loaded via a separate