Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop Haste #11303

Merged
merged 29 commits into from
Oct 24, 2017
Merged

Drop Haste #11303

merged 29 commits into from
Oct 24, 2017

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Oct 20, 2017

TODO

  • Fix Flow imports
  • Fix build
    • Fix UMD
    • Fix CJS
    • Fix FB
    • Fix RN
    • Fix size regressions
      • Fix DEV-only regression
  • Fix error extraction
  • Remove Jest providesModules things

Not sure how I feel about this.

This helps enforce that we have proper module boundaries between packages. It already exposes some renderers read directly from reconciler (and thus won't be accessible to third party renderers). So I think that is kind of nice.

This exposed an interesting issue with Jest and Yarn Workspaces (jestjs/jest#3830).

In a test, ../createReactNativeComponentClass and require('react-native-renderer').__SECRET_INTERNALS_DO_NOT_USE.createReactNativeComponentClass will be two different things. Because one is imported relatively (and thus is "located" in packages) while the other one is instantiated from code path going via node_modules/react-renderer. Jest thinks they are different.

This seemed like a show stopper to me at first. But then I thought, we don't like tests that do ../ anyway because they read internal modules. What if we just outlawed it?

In this case, you'd have to write react-native-renderer/src/createReactNativeComponentClass to get it in a test. Or read it from a secret bag exposed on react-native-renderer. But that might actually be okay because it makes "bad" code looks bad. All existing cases of this already have TODOs next to them (#11299). So we could forbid relative requires in tests. Thus also encouraging people to write integration tests for new features too.

We could also wait for Jest to fix it but I doubt that will happen soon since almost nobody else bumped into this.

Mostly seeking feedback if anyone has strong objections at this point. If not, I could further flatten the structure, forbid relative requires in tests, and then fix the build. I expect that Rollup configuration will be significantly simpler now that we can let it use Node resolving mechanism without silly whitelists like:

// these are the FBJS modules that are used throughout our bundles
const fbjsModules = [
'fbjs/lib/warning',
'fbjs/lib/invariant',
'fbjs/lib/emptyFunction',
'fbjs/lib/emptyObject',
'fbjs/lib/hyphenateStyleName',
'fbjs/lib/getUnboundedScrollPosition',
'fbjs/lib/camelizeStyleName',
'fbjs/lib/containsNode',
'fbjs/lib/shallowEqual',
'fbjs/lib/getActiveElement',
'fbjs/lib/focusNode',
'fbjs/lib/EventListener',
'fbjs/lib/memoizeStringOnly',
'fbjs/lib/ExecutionEnvironment',
'fbjs/lib/createNodesFromMarkup',
'fbjs/lib/performanceNow',
];

@gaearon gaearon mentioned this pull request Oct 20, 2017
@gaearon gaearon force-pushed the no-haste-2 branch 3 times, most recently from b12a4f6 to a5a9e5d Compare October 21, 2017 00:51
@sebmarkbage
Copy link
Collaborator

Not sure how I feel about this.

...goes on to list only benefits.

@gaearon
Copy link
Collaborator Author

gaearon commented Oct 21, 2017

I'll write something more comprehensive when I get it working :-)

@gaearon gaearon force-pushed the no-haste-2 branch 9 times, most recently from f44aa96 to fc8bd83 Compare October 24, 2017 16:50
@gaearon gaearon changed the title [RFC] Drop Haste Drop Haste Oct 24, 2017
@gaearon
Copy link
Collaborator Author

gaearon commented Oct 24, 2017

I don't understand what CI failure means. It builds locally. But on the server require.resolve('react') doesn't find anything. I expect it to resolve to node_modules/react per symlink. I cleared CI cache a few times.

This uncovered an interesting problem where ./b from package/src/a would resolve to a different instantiation of package/src/b in Jest.

Either this is a showstopper or we can solve it by completely fobbidding remaining /src/.
It seems we can't use relative requires in tests anymore. Otherwise Jest becomes confused between real file and symlink.
jestjs/jest#3830

This seems bad... Except that we already *don't* want people to create tests that import individual source files.
All existing cases of us doing so are actually TODOs waiting to be fixed.

So perhaps this requirement isn't too bad because it makes bad code looks bad.

Of course, if we go with this, we'll have to lint against relative requires in tests.
It also makes moving things more painful.
Shouldn't affect correctness (they were ignored) but fixes DEV size regression.
@gaearon gaearon requested review from bvaughn and trueadm October 24, 2017 21:25
@gaearon
Copy link
Collaborator Author

gaearon commented Oct 24, 2017

Should be ready for initial review.

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty large change-set. I skimmed it, paying more attention to the bits in scripts/rollup.

I also did a build-and-sync locally and ran Catalyst with it for a couple of minutes. Had to update ReactNative.js to point to the new haste module name (ReactNativeFiber-* -> ReactNativeRenderer-*) but otherwise it seemed...ok. (I would expect this sort of change to fail fast and hard, if it broke things.)

Left a couple of minor thoughts. Overall this looks like a nice cleanup. 👍

// TODO: direct imports like some-package/src/* are bad. Fix me.
const {
injectInternals,
} = require('react-reconciler/src/ReactFiberDevToolsHook');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about adding a lint rule for this?

Something like...

export default function(context) {
  return {
    Identifier(node) {
      if (node.name === 'require') {
        const path = node.parent.arguments[0].value;
        
        if (path.match(/^react.*\/src/)) {
	      context.report(node, 'Do not import project internals');
        }
      }
    }
  };
};

This would make us less likely to add more of these.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, definitely. On todo list above after we know the change itself works.


// Module provided by RN:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -0,0 +1 @@
module.exports = require('ReactCurrentOwner');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: I don't understand the purpose of scripts/rollup/shims/rollup/*

Update: Looks like it maps to a combination of forkedFBModules and getShims in modules.js. Still not entirely sure why it's necessary. Seems like it's related some some quirk/limitation of Rollup?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be comments close to getShims and its usage explaining.

The object-assign shim replaces object-assign reference in anything that depends on React with assign implementation exposed on React internals. This avoids ReactDOM UMD having to ship its own polyfill of object-assign while React UMD already includes one.

We use the www shims for overriding some modules in React. Basically we want to have some requires be "untouched" and actually require modules from www instead of our implementations in the open source. We can't just tell Rollup to turn require('react/src/ReactCurrentOwner') into require('ReactCurrentOwner') because then it will attempt to resolve it (and not find it). The trick with a shim file and a separate plugin to "transform" the original file to null in memory is the only reliable way I found to completely "rewire" these requires to external modules in Rollup.

Some of these hacks might fall away or become simpler after ES modules. CommonJS is afterthought in Rollup and it breaks some more unusual cases. It might be that we're in that territory, and migrating to ESM will allow to revisit and simplify those hacks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I saw the comments. I just didn't fully understand them I guess. The third paragraph above helps me understand a little.

},

/******* React DOM *******/
{
babelOpts: babelOptsReact,
label: 'dom-fiber',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we drop the -fiber suffix from these now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do in a follow up pass renaming those. Ideally I want this to be explicit and just use package names instead of labels. But that makes yarn build react match everything. Maybe it had to be exact match but then it's more typing. I figured let's update these later when we have a better idea of how to do this ergonomically. Not coupled to build changes per se.

entry: 'react-art',
global: 'ReactART',
externals: ['react'],
babel: opts =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This could have a more meaningful name? Maybe like babelOptionsHook? I dunno. Names are hard.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll probably do another pass renaming them in this PR. I oscillated between long and short names here and used short ones. But maybe I was just tired of complex config and tried to simplify the perception by using shorter words. I don't care strongly either way and will think about them again.

@@ -503,44 +507,68 @@ function createBundle(bundle, bundleType) {
}
}

const filename = getFilename(bundle.name, bundle.hasteName, bundleType);
const filename = getFilename(
bundle.output || bundle.entry,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this only exists to map react-dom/server to react-dom/server.node

Couldn't we simplify by just renaming react-dom/server.js to react-dom/server.node.js?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably yes. I wanted to avoid an explicit separate entry point because it feels like public API. But I guess we have .browser anyway so might as well do that.

@gaearon
Copy link
Collaborator Author

gaearon commented Oct 24, 2017

Moved other follow up items into #11275.

This is getting pretty painful to keep up to date now so I'd prefer landing and addressing follow ups later this week.

@gaearon gaearon mentioned this pull request Oct 24, 2017
19 tasks
@gaearon gaearon merged commit 1eed302 into facebook:master Oct 24, 2017
@bvaughn
Copy link
Contributor

bvaughn commented Oct 25, 2017

That's fine of course 👍 I'm always happy to iterate on stuff like that.

Ethan-Arrowood pushed a commit to Ethan-Arrowood/react that referenced this pull request Dec 8, 2017
* Use relative paths in packages/react

* Use relative paths in packages/react-art

* Use relative paths in packages/react-cs

* Use relative paths in other packages

* Fix as many issues as I can

This uncovered an interesting problem where ./b from package/src/a would resolve to a different instantiation of package/src/b in Jest.

Either this is a showstopper or we can solve it by completely fobbidding remaining /src/.

* Fix all tests

It seems we can't use relative requires in tests anymore. Otherwise Jest becomes confused between real file and symlink.
jestjs/jest#3830

This seems bad... Except that we already *don't* want people to create tests that import individual source files.
All existing cases of us doing so are actually TODOs waiting to be fixed.

So perhaps this requirement isn't too bad because it makes bad code looks bad.

Of course, if we go with this, we'll have to lint against relative requires in tests.
It also makes moving things more painful.

* Prettier

* Remove @providesModule

* Fix remaining Haste imports I missed earlier

* Fix up paths to reflect new flat structure

* Fix Flow

* Fix CJS and UMD builds

* Fix FB bundles

* Fix RN bundles

* Prettier

* Fix lint

* Fix warning printing and error codes

* Fix buggy return

* Fix lint and Flow

* Use Yarn on CI

* Unbreak Jest

* Fix lint

* Fix aliased originals getting included in DEV

Shouldn't affect correctness (they were ignored) but fixes DEV size regression.

* Record sizes

* Fix weird version in package.json

* Tweak bundle labels

* Get rid of output option by introducing react-dom/server.node

* Reconciler should depend on prop-types

* Update sizes last time
@gaearon gaearon deleted the no-haste-2 branch December 14, 2017 18:28
NMinhNguyen referenced this pull request in enzymejs/react-shallow-renderer Jan 29, 2020
* Use relative paths in packages/react

* Use relative paths in packages/react-art

* Use relative paths in packages/react-cs

* Use relative paths in other packages

* Fix as many issues as I can

This uncovered an interesting problem where ./b from package/src/a would resolve to a different instantiation of package/src/b in Jest.

Either this is a showstopper or we can solve it by completely fobbidding remaining /src/.

* Fix all tests

It seems we can't use relative requires in tests anymore. Otherwise Jest becomes confused between real file and symlink.
jestjs/jest#3830

This seems bad... Except that we already *don't* want people to create tests that import individual source files.
All existing cases of us doing so are actually TODOs waiting to be fixed.

So perhaps this requirement isn't too bad because it makes bad code looks bad.

Of course, if we go with this, we'll have to lint against relative requires in tests.
It also makes moving things more painful.

* Prettier

* Remove @providesModule

* Fix remaining Haste imports I missed earlier

* Fix up paths to reflect new flat structure

* Fix Flow

* Fix CJS and UMD builds

* Fix FB bundles

* Fix RN bundles

* Prettier

* Fix lint

* Fix warning printing and error codes

* Fix buggy return

* Fix lint and Flow

* Use Yarn on CI

* Unbreak Jest

* Fix lint

* Fix aliased originals getting included in DEV

Shouldn't affect correctness (they were ignored) but fixes DEV size regression.

* Record sizes

* Fix weird version in package.json

* Tweak bundle labels

* Get rid of output option by introducing react-dom/server.node

* Reconciler should depend on prop-types

* Update sizes last time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants