Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Path Aliasing? #16

Open
seunlanlege opened this issue Dec 11, 2017 · 8 comments
Open

Path Aliasing? #16

seunlanlege opened this issue Dec 11, 2017 · 8 comments

Comments

@seunlanlege
Copy link

the generated code doesn't fully resolve paths specified in the tsconfig.json

@demoran23
Copy link

I've been struggling with this as well.

Actual behavior

  1. Run tsc with no errors
  2. Start the package manager with --reset-cache (eg yarn run start --reset-cache)
  3. An error is generated in the console and displayed on the device on a red screen

Error details

error: bundling failed: "Unable to resolve module @services/notification from C:\\Users\\adams\\Source\\Repos\\<redacted>\\built\\index.js: Module does not exist in the module map\n\nThis might be related to https://github.com/facebook/react-native/issues/4968\nTo resolve try the following:\n 1. Clear watchman watches: watchman watch-del-all.\n 2. Delete the node_modules folder: rm -rf node_modules && npm install.\n 3. Reset packager cache: rm -fr $TMPDIR/react-* or npm start -- --reset-cache."

Configuration

tsconfig.json

{
  "include": ["./src/"],
  "compilerOptions": {
    "target": "es2015",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "es2015",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "allowJs": true,                       /* Allow javascript files to be compiled. */
    "jsx": "react",                           /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "sourceMap": true,                        /* Generates corresponding '.map' file. */
    "outDir": "./built",                        /* Redirect output structure to the directory. */
    "strict": true,                            /* Enable all strict type-checking options. */
    "noImplicitAny": false,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": "./src",                       /* Base directory to resolve non-absolute module names. */
    "skipLibCheck": true
  }
}

.babelrc

{
  "presets": [
    "react-native"
  ]
}

Example files

src/@services/notification/index.ts

/**
 * @providesModule @services/notification
 */

import { NotificationCodeToNotificationConfigMap } from "@services/notification/configMap";
import { get } from "lodash";
import { Actions } from "react-native-router-flux";

interface IAdditionalData {
  [key: string]: string;
}

export interface IOneSignalNotification {
  payload: IAdditionalData;
}

export interface INotificationConfig {
  readonly itemIdPropName?: string;
  readonly sceneKey: string;
  readonly backupSceneKey?: string;
  transformAsync?(notification: any): Promise<any>;
}

export const route = async (notification: IOneSignalNotification) => {
  // We expect the payload to have an item type and an item id
  const { NotificationCode } = get(notification, "payload.additionalData");
  let useBackupSceneKey: boolean = false;

  // If we know how to handle that item type, route to it
  const config: INotificationConfig = NotificationCodeToNotificationConfigMap[NotificationCode];
  if (config && config.sceneKey) {
    const { itemIdPropName, transformAsync, sceneKey, backupSceneKey } = config;

    const props = {};

    // If we have an item id prop name, let's try and transform our item id and use it
    if (itemIdPropName && transformAsync) {
      props[itemIdPropName] = await transformAsync(notification);

      // But if we failed to transform, use the backup scene key
      if (!props[itemIdPropName] && backupSceneKey) {
        useBackupSceneKey = true;
      }
    }

    const actualizedSceneKey: string = useBackupSceneKey && backupSceneKey ? backupSceneKey : sceneKey;

    console.log(`Routing to scene ${actualizedSceneKey} with props:`, props);

    Actions[actualizedSceneKey](props);
  }
};

built/@services/notification/index.js

/**
 * @providesModule @services/notification
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { NotificationCodeToNotificationConfigMap } from "@services/notification/configMap";
import { get } from "lodash";
import { Actions } from "react-native-router-flux";
export const route = (notification) => __awaiter(this, void 0, void 0, function* () {
    // We expect the payload to have an item type and an item id
    const { NotificationCode } = get(notification, "payload.additionalData");
    let useBackupSceneKey = false;
    // If we know how to handle that item type, route to it
    const config = NotificationCodeToNotificationConfigMap[NotificationCode];
    if (config && config.sceneKey) {
        const { itemIdPropName, transformAsync, sceneKey, backupSceneKey } = config;
        const props = {};
        // If we have an item id prop name, let's try and transform our item id and use it
        if (itemIdPropName && transformAsync) {
            props[itemIdPropName] = yield transformAsync(notification);
            // But if we failed to transform, use the backup scene key
            if (!props[itemIdPropName] && backupSceneKey) {
                useBackupSceneKey = true;
            }
        }
        const actualizedSceneKey = useBackupSceneKey && backupSceneKey ? backupSceneKey : sceneKey;
        console.log(`Routing to scene ${actualizedSceneKey} with props:`, props);
        Actions[actualizedSceneKey](props);
    }
});
//# sourceMappingURL=index.js.map

Insights

Background

I have an existing javascript project, and I'm trying to bring Typescript into the fold,
so I had a branch that I was doing my Typescript init on. I took some of my recent work and changed the files from js to ts. These files work fine when they're js - it's only when they're ts that I have a problem.

Attempted workarounds

@providesModule

This appears to have worked in at least once instance, but isn't consistently working.

nested package.json files

This is our the majority of my inherited code works. For example, without Typescript, to get absolute pathing working we placed a package.json file in the src/@services folder, like so:

{
  "name": "@services"
}

Even with these files in place, the ts files cause the module not to be found. (though as I check the emitted code, it appears that these nested package.json files do not appear in the built tree. - this is another avenue of attack I'll try)

@demoran23
Copy link

demoran23 commented Dec 24, 2017

Action

So I tried to put package.json files into the built directory and see what happened.

Move package.json files from src to built

pushd src; find . -name 'package.json' -exec cp -iv --parents -t ../built {} +; popd

Start the packager and refresh the app

yarn run start --reset-cache

Result

error: bundling failed: "Unable to resolve module @navigation from C:\\Users\\adams\\Source\\Repos\\<redacted>\\built\\index.js: Module does not exist in the module map\n\nThis might be related to https://github.com/facebook/react-native/issues/4968\nTo resolve try the following:\n 1. Clear watchman watches: watchman watch-del-all.\n 2. Delete the node_modules folder: rm -rf node_modules && npm install.\n 3. Reset packager cache: rm -fr $TMPDIR/react-* or npm start -- --reset-cache."

This was a bit unexpected. @src/navigation doesn't have any Typescript files in it at all.

Removing @built/navigation/package.json and restarting prevented this from occurring.

Cleanup

Remove package.json files from built

rm built/**/package.json

@Li357
Copy link

Li357 commented Jun 22, 2018

Any updates on this? I'm trying to use TS with an ejected app from CRNA, but it won't resolve my path aliases

@gordysc
Copy link

gordysc commented Aug 5, 2018

For other folks, I did find this helpful for absolute paths:
https://www.npmjs.com/package/react-native-typescript-transformer

I too struggled with the alias pathing issue as mentioned above, but at least with this I could do:
src/<folder name>/... rather than ../../../<folder name> from other components

@sivagao
Copy link

sivagao commented Sep 3, 2018

any updates?

@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
    }
});

@Symous
Copy link

Symous commented May 31, 2019

@vacoo solution works for me, but just a friendly tip: you may need to re-install the app to make it work.

@samih-dev
Copy link

samih-dev commented Oct 3, 2019

Thanks @vacoo, worked for me.

sharing my config as it includes also the ts files and special case using Alias for ONE ts file:

tsconfig.json path property

"paths": {
      "@app/interfaces": [
        "src/interfaces"
      ],
      "@app/enums": [
        "src/enums"
      ],
      "@app/shared/*": [
        "src/shared/*"
      ],
      "@app/styles/*": [
        "src/styles/*"
      ]
    }

babel.config.js:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['./src'],
        extensions: ['.tsx', '.ts'],
        alias: {
          '@app/enums': './src/enums.ts',
          '@app/interfaces': './src/interfaces.ts',
          '@app/shared': './src/shared',
          '@app/styles': './src/styles',
        },
      },
    ],
  ],
};
  • installing the dev dependency: babel-plugin-module-resolver

P.S - I did this with default ts template provided with react-native CLI - not this Microsft template

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

No branches or pull requests

8 participants