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

Support for React Native #29

Closed
adamterlson opened this issue Feb 22, 2016 · 70 comments
Closed

Support for React Native #29

adamterlson opened this issue Feb 22, 2016 · 70 comments

Comments

@adamterlson
Copy link

Hey there. This is not an issue per say, more a cry for help.

Have you by some crazy chance attempted to use this plugin with React Native? It simply just doesn't seem to work. I opened a Stack Overflow issue about this. Any help would be greatly appreciated! If you're at a loss as I am, please feel free to ignore and close this issue.

@tleunen
Copy link
Owner

tleunen commented Feb 22, 2016

To be honest, I never tried it on react-native because I still never used Native yet.
But from the error you're reporting

Unable to resolve module app/components/MyComponent from .... Invalid directory /Users/node_modules/app/components/MyComponent.

It looks like the plugin doesn't transform the path correctly. The path points to Users/node_modules. It looks very weird.

Do you have by any chance a small project to easily reproduce the issue?

@adamterlson adamterlson changed the title Supprot for React Native Support for React Native Feb 22, 2016
@adamterlson
Copy link
Author

Hey there, I created a test project: https://github.com/adamterlson/babeltest

Thank you for your help!

By the way, if you like React and have interest in native app development at all, check out React Native. It's super awesome. :)

@tleunen
Copy link
Owner

tleunen commented Feb 23, 2016

I am very interested in React Native ;-) Just couldn't have the time to really dig into it yet.

Thanks for the setup though, I'll try to see what I can do to add the RN support.

@tleunen
Copy link
Owner

tleunen commented Feb 23, 2016

After a quick investigation, it seems the plugin is never used. That's why the path is not updated and RN cannot find the module at app/MyComponent.

I'm still not sure yet how to properly debug the babel config/loader inside react native.

@adamterlson
Copy link
Author

Yeah, I am not sure how to do that either. Wonder why it's not used... Hmmm. Thanks for looking into it though!

@datapimp
Copy link

@adamterlson i don't believe it will work with react native because react-native has its own module aliasing and resolution approach based on their own dependency graph tools

see:

https://github.com/facebook/fbjs#usage

https://github.com/facebook/fbjs/blob/master/scripts/babel/rewrite-modules.js

@adamterlson
Copy link
Author

@datapimp Well that's a bummer. Good find, thank you for the information!

So I take it then that the fbjs babel rewrite-modules comes first before the user-specified babel plugins? Sure would be nice if it were possible to reverse that order....

@tleunen
Copy link
Owner

tleunen commented Feb 28, 2016

Yep, from my investigation, it comes first so the plugin is not even executed because the path is not correct. I opened a ticket in react-native to see what we could do. facebook/react-native#6118

@adamterlson
Copy link
Author

Awesome, thank you very much @tleunen.

@adamterlson
Copy link
Author

So after reading through the issue thread (facebook/react-native#6118) it seems not part of the game plan to add this before/after hook any time soon, and at any rate, the issue is not with module-alias.

They recommend not to use ProvidesModule; however, I needed to move forward so I thought I'd share what I did in case anyone else finds this thread and wants to live dangerously.

First, I'm assuming that once fbjs is removed from RN (which is planned and probably why they don't want people using providesModule) then module-alias will work as expected. I did some experimenting and found that providesModule doesn't just take "MyModule" as in the example, you can give it any string, including something that looks like a path that corresponds to its location:

/*
 * @providesModule app/redux/modules/user
 */

In this way, I can actually require this module from a component in the precise way that module-alias would allow:

import * as UserActions from 'app/redux/modules/user';

So, once fbjs goes away, I'll delete all the providesModule lines and it should just keep working with an alias set up for "app".

That's the theory, anyway. Thank you @tleunen and @datapimp for your help!

@adamterlson
Copy link
Author

Is it now possible? facebook/react-native@e6cb02d

@tleunen
Copy link
Owner

tleunen commented Mar 10, 2016

Hmm.. Maybe.. If someone can test it. Otherwise I'll try to find some time to give it a try

@alexbepple
Copy link

@tleunen, @adamterlson It still does not work. The RN packager somehow interferes and tries to resolve relative to node_modules/react-native/packager.

I added this to my .babelrc:

  "plugins": [
    ["babel-plugin-module-alias", [
      {"src": "./foo", "expose": "foo"}
    ]]
  ]

And this to index.android.js:

import 'foo/bar'

The result:
screen shot 2016-03-29 at 23 59 47

For comparison: if I add import 'foo/bar' to a file one level down, e.g. src/index.js, the error messages reflects that (the two dots are new):
screen shot 2016-03-30 at 00 04 47

So if we could somehow prevent the RN packager from adding the node_modules/react-native/packager bit …

Sorry that I am so non-technical here. I just do not have sufficient understanding of what is going on here to be more precise.

I tested with RN 21.

@alexbepple
Copy link

Figured out how that path gets in there: It is just because react-native/packager/launchPackager.command changes the cwd. If I manually start the packager from the project dir (./node_modules/react-native/packager/packager.sh start), a similar error message appears – with the correct path, however:
screen shot 2016-03-30 at 00 29 14

Wild guess: when the RN packager builds the in-memory fs for JavaScript it only includes the code that it considers relevant. But at that point in time, babel-plugin-module-alias hadn't had the chance to transform the path yet. So the packager thinks that the module isn't there.

@tleunen
Copy link
Owner

tleunen commented Apr 8, 2016

Is there any solution we can do?

@thatsmydoing
Copy link

It works fine for me with RN 0.24 on Linux.

My .babelrc is

{
  "presets": [
    "react-native"
  ],
  "plugins": [
    ["babel-plugin-module-alias", [
      { "src": "./src/lib", "expose": "lib" }
    ]]
  ]
}

And it works with just a regular npm start. I guess it's completely fixed now?

@tleunen
Copy link
Owner

tleunen commented Apr 20, 2016

That's awesome! @adamterlson can you confirm it works for you as well?

@adamterlson
Copy link
Author

Yup! Confirmed! 🎉

@consideRatio
Copy link

@adamterlson @thatsmydoing: You used the babel-preset-react-native npm-package, correct?

Did you by any chance also make Flow understand how to use this package?

@thatsmydoing
Copy link

Yes, react-native preset is the only other thing I have in my babelrc. I haven't really tried to use flow, so I can't help there

@adamterlson
Copy link
Author

@consideRatio Sorry I haven't had much luck using flow for RN. I get all sorts of problems. Sorry I can't help!

@corbt
Copy link

corbt commented Jun 6, 2016

I got this to work with Flow by using this package and then additionally symlinking my src file into node_modules. This plugin satisfies the packager, and the symlink satisfies Flow.

@pewh
Copy link

pewh commented Jun 27, 2016

Previously this module was worked for me with RN 0.25.
But when I upgrade to React Native 0.27.2 with React 15.1.0, it didn't work again.

screenshot_2016-06-27_16-52-52

Had anyone confirmed with RN 0.27?

Edit:
I have upgrade to latest version, and the problem still exist.
Below is my .babelrc

{
  "passPerPreset": true,
  "presets": [
    {
      "plugins": [
        "./server/schema/babelRelayPlugin"
      ]
    },
    "react-native"
  ],
  "plugins": [
    "transform-decorators-legacy",
    ["module-alias", [
      { "src": "./src/components",         "expose": "~shared-components" },
      { "src": "./src/constants",          "expose": "~constants" },
      { "src": "./src/helpers",            "expose": "~utils" },
      { "src": "./src/modules",            "expose": "~modules" },
      { "src": "assets",                   "expose": "~assets" }
    ]]
  ]
}

Edit:
I think there's a correlation with facebook/react-native#6118 (comment))

@tleunen
Copy link
Owner

tleunen commented Jun 27, 2016

Could you try with the previous version to see if it's a new issue with the latest version of the plugin?

@pewh
Copy link

pewh commented Jun 28, 2016

It's work on babel-plugin-module-alias version 1.4 with RN 0.25 (+ React 0.14).
And it doesn't work on babel-plugin-module-alias version 1.4 with RN 0.27.2 and babel-plugin-module-alias latest version (1.5) with RN 0.27.2. FYI RN with version >= 0.27 must use React 15, which I guess it use different providesModules (facebook/react-native#6118 (comment))

@pewh
Copy link

pewh commented Jun 28, 2016

I don't know what I did exactly, but now I don't get this issue again 🎉 LOL

@schoettler
Copy link

schoettler commented Aug 11, 2016

@pewh Can you share your versions on package.json? I'm having this issue now with react-native: 0.31.0 and babel-plugin-module-alias: 1.6.0, my .babelrc looks like this:

{
  "presets": [
    "react-native"
  ],
  "plugins": [
    ["module-alias", [
      { "src": "./src/foo", "expose": "foo" },
      { "src": "./src/bar", "expose": "bar" }
    ]]
  ]
}

When I try import file from 'foo/file' it's looking within /node_modules still

UPDATE: Deleted the .babelrc file to make sure RN packager was reading it, got the expected file not found error, when I reincorporated it, worked. :l

@svicalifornia
Copy link

@peto85 @sercanov It's not working for me (RN 0.42.3). May I ask what's in your .babelrc file?

@AlanFoster
Copy link

AlanFoster commented Apr 12, 2017

For @svicalifornia and folk wanting to get this up and running with react-native, running babel-plugin-module-resolver version 2.7.0 and react-native 0.43.0

yarn add babel-plugin-module-resolver --dev

Note - this is not "babel-plugin-module-alias", which is capped at version 1.6.0

{
  "presets": [
    "react-native"
  ],
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["./src"],
        "alias": {
          "components": "./components"
        }
      }
    ]
  ]
}

Note -This tool goes under plugins, and not presets.


With the above configuration it assumes a directory structure of:

.YourApplication
├── android
├── app.json
├── index.android.js
├── index.ios.js
├── ios
├── node_modules
├── package.json
├── readme.md
├── src
    ├── components          // Your custom components that we provided an alias for
         ├── button                  
                   ├── index.js 
├── tests
├── yarn-error.log
└── yarn.lock

You can then import your custom components anywhere within your react-native application with:

import Button from 'components/button';

Note - You may need to clear your cache in some cases:

npm run start -- --reset-cache

@ghost
Copy link

ghost commented Apr 20, 2017

@svicalifornia my .babelrc is quite simple:

{
"presets": ["react-native"],
"plugins": [
    ["module-resolver", {
      "root": ["./"],
      "alias": {
        "src": "./src"
      }
    }]
  ]
}

@wswoodruff
Copy link

wswoodruff commented Apr 27, 2017

Still running into this issue when using require('myAliasName') and my .baberc looks very similar to peto85's

@llaine
Copy link

llaine commented May 8, 2017

Still having this issue using RN 0.44 + webpack

@bernatfortet
Copy link

bernatfortet commented May 30, 2017

@AlanFoster I used your solution and it's great until it fails.

Resetting the cache (npm run start -- --reset-cache) works for me but if I start the packager from xCode then it fails again.

Could you explain more about: "Note - You may need to clear your cache in some cases:"

@yenbekbay
Copy link

You might consider using Haul instead of react-native-packager (or metro-bundler as it's called now). It doesn't require resetting cache to work with this plugin.

@jean9696
Copy link

jean9696 commented Jun 9, 2017

Same issue here, I'm on windows...

{
  "presets": ["react-native"],
  "plugins": [
    ["module-resolver", {
      "root": ["./src", "./core"]
    }]
  ]
}

@emusgrave
Copy link

I think the core of the issue is that when React.xcodeproj automatically launches the Packager, it does so from the directory of the packager. Any sane person is going to launch it from the root of their project, and that is where all of the module-resolver paths are starting from. To compound the issue, once you've launched it the "incorrect" way once, then there are cached paths that interfere with you once you try to launch it elsewhere. That's why --reset-cache is a popular answer to the problem, although it doesn't fix the core issue.

Here's what I did to sleep better at night:

  1. Leave React.xcodeproj and everything else in node_modules alone.

  2. Make a copy of launchPackager.command and packager.sh (from /node_modules/react-native/packager) and drop them into my project source tree

  3. Edit packager.sh so that it changes to your project root dir and then launches the react-native cli from there. ALSO, I added the --reset-cache parameter so that anytime I want to reset the cache I just need to close out the long-standing React Packager terminal instance. The next program run will relaunch and reset the cache.

    The new packager.sh (adjust your relative path from THIS_DIR to your project root as necessary. $THIS_DIR will point to wherever you put the packager.sh file):

    THIS_DIR=$(dirname "$0")
    pushd "$THIS_DIR/../../../"
    pwd
    node "./node_modules/react-native/local-cli/cli.js" start --reset-cache "$@"
    popd
    
  4. Here's the key: We want to launch the packager before React.xcodeproj does it automatically. So, what I did was put a Pre-Action in my Scheme in Xcode. Edit Scheme -> Run -> Pre-Actions, Then add the following for the script (copied from React.xcodeproj, but with the path to launchPackager.command changed)

    if [ -z "${RCT_NO_LAUNCH_PACKAGER+xxx}" ] ; then
    if nc -w 5 -z localhost 8081 ; then
    if ! curl -s "http://localhost:8081/status" | grep -q "packager-status:running" ; then
    echo "Port 8081 already in use, packager is either not running or not running correctly"
    exit 2
    fi
    else
    open "$SRCROOT/../src/platforms/native/launchPackager.command" || echo "Can't start packager automatically"
    fi
    fi
    

Please note that your path to launchPackager.command will likely be different.

After doing all of this, now my module-resolver settings all work correctly when I let Xcode launch the packager. Prior to this, I was just always keeping it launched from my own terminal window and anytime I forgot to do that I would have to close out the auto-launched copy of packager and run a --reset-cache to get things working again. Easy enough for me to do, but a hassle and not easy to explain to other dev's working on the project.

Next step: convince the React-native guys to change their launch point of the packager so that none of this is necessary.

@nstr
Copy link

nstr commented Sep 4, 2017

For me works this one:

step 1
watchman watch-del-all && rm -rf $TMPDIR/react-* && rm -rf node_modules/ && npm cache clean && npm install && npm start -- --reset-cache

step 2
Reload iphone simulator, for my case it is
Cmd R

Just take note:
You can run project only in this way npm start.
Command react-native run-ios does not work with aliases.

@SirNeuman
Copy link

I cannot get this to work with react-native run-android either (as others have said react-native run-ios does not work) when setting root to ./app. I get an error saying that my application code is trying to import from path_to_project/node_models/react-native/app, which it should not be. So i get around this by setting root to ../../app when i use react-native run-android, however when building my APK i get that the module is trying to import from a different starting point (the correct one). So for building my APK i have to set root to ./app.
So for now this is what my .babelrc file looks like:

{
  "presets": ["react-native"],
  "env": {
    "production": {
      "plugins": [
        ["module-resolver", {
            "root": ["./app"],
            "extensions": [".js", ".ios.js", ".android.js"],
            "alias": {
              "MyActions": "./app/actions",
               ...
            }
          }
        ]
      ]
    },
    "development": {
       "plugins": [
          ["module-resolver", {
            "root": ["../../app"],
            "extensions": [".js", ".ios.js", ".android.js"],
            "alias": {
              "MyActions": "../../app/actions"
            }
          }]
        ]
    }
  },

}

Though this is kind of awkward...

@tayfunyasar
Copy link

tayfunyasar commented Sep 29, 2017

Its not working with "require" fn but works with "import".

.babelrc

{
  "presets": ["react-native"],
  "plugins": [
    ["module-resolver", {
      "root": ["./app"],
      "alias": {
        "~": "/",

        "@images": "/images",
        "@data": "/data",

        "underscore": "lodash"
      }
    }]
  ]
}

package.json

"devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-eslint": "^7.2.3",
    "babel-jest": "20.0.3",
    "babel-plugin-module-resolver": "^2.7.1",
    "babel-preset-react-native": "^2.0.0",
    "eslint": "^4.6.1",
    "eslint-config-airbnb": "^15.1.0",
    "eslint-import-resolver-babel-module": "2.2.1",
    "eslint-plugin-babel": "^4.1.2",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-jsx-a11y": "^6.0.2",
    "eslint-plugin-prefer-object-spread": "^1.2.1",
    "eslint-plugin-react": "^7.2.1",
    "eslint-plugin-react-native": "^3.1.0",
   ....
  }

Works with import at top:
import I18n from '~/localization';
not working with require:
<Image source={require('@images/test.jpg')} />

@tleunen
Copy link
Owner

tleunen commented Sep 29, 2017

@tayfunyasar What if you move your require at the top and use the variable in your component instead?

@tayfunyasar
Copy link

tayfunyasar commented Sep 30, 2017

Thanks for your answer @tleunen.

Its still not working with require. I also tried import it, it worked with it.

worked:
import gradientBg from '~/images/test.png';
still not working:
var gradientBg = require('~/images/test.png');
error message:

Unable to resolve module '//images/test.png' from '#MyProjectDir#/app/component/xxx/index.js' could not resolve '//images/test.png' as a folder: it did not contain a package, not an index file

@Pensarfeo
Copy link

Still not working with the following configuration on windows and react native 0.47.2

{
  "presets": ["react-native"],
  "plugins": [
    [
      "module-alias", {
        "root": ["./app"],
        "alias": {
          "screens": "./screens"
        },
        "extensions": [".js", ".ios.js", ".android.js"]
      }
    ]
  ]
}

I finally "solved" the problem by adding package.json in the app/screens folder with

{name: "screens"}

and declaring that folder as a local dependency in my main package.json.

@PavanGangireddy
Copy link

@emusgrave after following the steps you have mentioned we got this error in xcode.
"React/RCTLog.h file is not found" please look into that, give us any leads if anyone resolved this error...

@sdeleon28
Copy link

I was having the issue where the packager would be looking for babel-plugin-module-resolver aliases inside node_modules/. @emusgrave's comment made me realize I had been using the wrong npm script for start. For some reason, in the old version of my codebase, I had it as:

    "start": "./node_modules/react-native/packager/packager.sh",

When I updated RN, that packager/ folder was no longer there, but I realized there was scripts/packager.sh script, so I changed it to:

    "start": "./node_modules/react-native/scripts/packager.sh",

This was my mistake. It was this script that was making the packager pick up aliases from inside node_modules/. I was able to solve this by changing it back to:

    "start": "node ./node_modules/react-native/local-cli/cli.js start",

@Kouznetsov
Copy link

Kouznetsov commented Jan 11, 2018

Coming way after the battle, but still.

I managed to finally make it work by adding ".js" to the extensions list and clearing the cache when running metro. Below is my .babelrc :

{
  "plugins": [
    ["module-resolver", {
      "root": ["./app/**", "./assets/**"],
      "extensions": [".js"]
    }]
  ],
  "presets": ["react-native"]
}

Then starting metro by running react-native start -- --reset-cache

@DiederikvandenB
Copy link

So if I understand correctly, it is not possible to use this plugin with react-native run-ios? You really have to run react-native start -- --reset-cache every time?

@Kouznetsov
Copy link

Resetting the cache once did the trick for me.

@unidwell
Copy link

unidwell commented Mar 15, 2018

(tested with RN 0.52.0 and module-resolver babel plugin)

For react-native cli command, the working directory for Babel plugins is a directory ./node_modules/react-native and not the project's root (i.e. where the .babelrc file resides). The result is that module-resolver incorrectly resolves all paths from the misplaced project's root. As a workaround one can rewrite the relative paths a little bit. Each dot "." should be rewritten as "../.." to help with the path resolution.

For instance, the following babel configuration (check root parameter):

// .babelrc
{
  "presets": ["react-native"],
  "plugins": [
    ["module-resolver", {
      "root": [".", "./src"]
    }]
  ]
}

should be:

// .babelrc
{
  "presets": ["react-native"],
  "plugins": [
    ["module-resolver", {
      "root": [".", "./src", "../..", "../../src"]
    }]
  ]
}

The original paths can be left as they are, as some IDEs (VB Code + RN extension) have their own Metro builder with properly configured project's working directory and no issues with module-resolver babel plugin.

@sturmenta
Copy link

Reporting:

To react native use the package.json fix, after having run
react-native start - --reset-cache
("start": "node./node_modules/react-native/local-cli/cli.js start ")

To reactjs it worked: https://github.com/entwicklerstube/babel-plugin-root-import

Greetings ✌

@xstable
Copy link

xstable commented Feb 8, 2019

@emusgrave could you please show, where you place your files?
I don't get it to work.

I've placed the two files into /ios/-Directory.
My Packager.sh looks like this:

THIS_DIR=$(dirname "$0")
pushd "$THIS_DIR/../"
pwd
node "./node_modules/react-native/local-cli/cli.js" start --reset-cache "$@"
popd

and adjusted the Run-Script like that:

image

I'm not sure, where the $SRCROOT points to.

@vacoo
Copy link

vacoo commented Apr 16, 2019

I am solve this problem. Just add to metro.config.js attribute resetCache: true

package.json.

{
    "name": "myApp",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "start": "node node_modules/react-native/local-cli/cli.js start",
        "android": "react-native run-android",
        "test": "jest"
    },
    "dependencies": {
        "react": "16.8.3",
        "react-native": "0.59.4"
    },
    "devDependencies": {
        "@babel/core": "^7.4.3",
        "@babel/runtime": "^7.4.3",
        "@types/jest": "^24.0.11",
        "@types/react": "^16.8.13",
        "@types/react-native": "^0.57.43",
        "@types/react-test-renderer": "^16.8.1",
        "babel-jest": "^24.7.1",
        "babel-plugin-module-resolver": "^3.2.0",
        "jest": "^24.7.1",
        "metro-react-native-babel-preset": "^0.53.1",
        "react-native-typescript-transformer": "^1.2.12",
        "react-test-renderer": "16.8.3",
        "ts-jest": "^24.0.2",
        "typescript": "^3.4.3"
    },
    "jest": {
        "preset": "react-native",
        "moduleFileExtensions": [
            "ts",
            "tsx",
            "js"
        ],
        "transform": {
            "^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
            "\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
        },
        "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
        "testPathIgnorePatterns": [
            "\\.snap$",
            "<rootDir>/node_modules/"
        ],
        "cacheDirectory": ".jest/cache"
    }
}

metro.config.js. Add resetCache: true

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: false,
        resetCache: true
      },
    }),
  },
};

tsconfig.json

{
  "compilerOptions": {
       "baseUrl": "./src",
            "paths": {
                   "@components/*": ["components/*"]
             },
    }
}

babel.config.js

module.exports = {
    presets: ["module:metro-react-native-babel-preset"],
    plugins: [
        [
            "module-resolver",
            {
                root: ["./src"],
                extensions: [".tsx"],
                alias: {
                    "@components": "./src/components"
                }
            }
        ]
    ]
};

App.tsx

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React from "react";
import { Component } from "react";
import { Platform, StyleSheet, Text, View } from "react-native";
import Buble from "@components/buble";

const instructions = Platform.select({
    ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
    android: "Double tap R on your keyboard to reload,\n" + "Shake or press menu button for dev menu"
});

type Props = {};
export default class App extends Component<Props> {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>Welcome to React Native! 200</Text>
                <Text style={styles.instructions}>To get started, edit App.js</Text>
                <Text style={styles.instructions}>{instructions}</Text>
                <Buble />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "#F5FCFF"
    },
    welcome: {
        fontSize: 20,
        textAlign: "center",
        margin: 10
    },
    instructions: {
        textAlign: "center",
        color: "#333333",
        marginBottom: 5
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests