kristerkari / react-native-sass-transformer

Use Sass to style your React Native apps.
MIT License
222 stars 19 forks source link

Styles are imported as empty object with RN 73 #106

Open vivtung opened 6 months ago

vivtung commented 6 months ago

I am importing the styles in the following way but when I console.log(styles), it is returning{} import styles from '#/screens/auth/SignIn.scss';

I have attached my files.

metro.config.js

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname);

config.transformer.babelTransformerPath = require.resolve("react-native-sass-transformer")
config.resolver.sourceExts.push("scss");
config.resolver.sourceExts.push("sass");

module.exports = config;

babel.config.js

module.exports = function(api) {
  api.cache(true);

  return {
    plugins: [
      ['module:react-native-dotenv'],
      'react-native-reanimated/plugin',
      '@babel/plugin-proposal-export-namespace-from',
    ],
    presets: [['babel-preset-expo']],
  };
};

app.json

{
  "expo": {
    "version": "3.3.2",
    "privacy": "public",
    "android": {
      "icon": "./assets/icon.png",
      "package": "main",
      "permissions": [
        "LOCATION",
        "CAMERA",
        "MEDIA_LIBRARY",
        "AUDIO_RECORDING",
        "RECORD_AUDIO",
        "READ_EXTERNAL_STORAGE",
        "WRITE_EXTERNAL_STORAGE",
        "POST_NOTIFICATIONS",
        "PUSH_NOTIFICATIONS",
        "NOTIFICATIONS",
        "ACCESS_BACKGROUND_LOCATION",
        "ACCESS_COARSE_LOCATION",
        "ACCESS_FINE_LOCATION",
        "FOREGROUND_SERVICE"
      ],
      "versionCode": 332,
      "googleServicesFile": "./google-services.json"
    },
    "assetBundlePatterns": [
      "assets/**/*"
    ],
    "description": "",
    "web": {
      "bundler": "metro"
    },
    "packagerOpts": {
      "config": "metro.config.js",
      "sourceExts": ["ts", "tsx", "scss", "sass"]
    },
    "ios": {
      "buildNumber": "3.3.2",
      "config": {
        "usesNonExemptEncryption": false
      },
      "icon": "./assets/notification-icon.png",
      "infoPlist": {
        "LSApplicationQueriesSchemes": [
          "comgooglemaps",
          "citymapper",
          "uber",
          "lyft",
          "waze"
        ],
        "UIBackgroundModes": [
          "location",
          "fetch"
        ],
        "NSLocationAlwaysAndWhenInUseUsageDescription": "App requires geolocation to improve the quality of the service",
        "NSLocationAlwaysUsageDescription": "App requires geolocation to improve the quality of the service",
        "NSLocationWhenInUseUsageDescription": "App requires geolocation to improve the quality of the service"
      },
      "bundleIdentifier": "main"
    },
    "name": "Arelli",
    "orientation": "portrait",
    "platforms": [
      "android",
      "ios"
    ],
    "scheme": "main",
    "slug": "main",
    "updates": {
      "fallbackToCacheTimeout": 10000,
      "url": "https://u.expo.dev/13ced000-95fd-4620-b3f7-09c1a2e0651b"
    },
    "splash": {
      "image": "./assets/splashscreen.png",
      "backgroundColor": "#292929"
    },
    "notification": {
      "icon": "./assets/push/icon-notification.png",
      "color": "#000000"
    },
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/push/icon-notification.png",
          "color": "#000000"
        }
      ],
      "sentry-expo",
      "expo-font"
    ],
    "runtimeVersion": "1.0.0"
  }
}

package.json


  "name": "main",
  "version": "3.2.3",
  "private": true,
  "main": "sources/index.tsx",
  "scripts": {
    "codestyle:check": "prettier --check \"mocks/**/*.ts\" \"sources/**/*.{ts,tsx}\"",
    "codestyle:fix": "prettier --write \"mocks/**/*.ts\" \"sources/**/*.{ts,tsx}\"",
    "lint:check": "tslint --project tsconfig.lint.json",
    "lint:fix": "tslint --fix --project tsconfig.lint.json",
    "start": "expo start --dev-client",
    "test": "jest --passWithNoTests",
    "test:coverage": "jest --coverage --passWithNoTests",
    "typecheck": "tsc",
    "android": "react-native run-android",
    "ios": "react-native run-ios"
  },
  "dependencies": {
    "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
    "@expo/react-native-action-sheet": "^3.12.0",
    "@react-native-async-storage/async-storage": "1.21.0",
    "@react-native-community/datetimepicker": "7.6.1",
    "@react-native-community/netinfo": "11.1.0",
    "@react-native-community/slider": "4.4.2",
    "@react-navigation/native": "^5.9.2",
    "@react-navigation/stack": "^5.14.2",
    "@reduxjs/toolkit": "^1.8.1",
    "@sentry/react-native": "5.19.1",
    "axios": "^0.27.2",
    "babel-plugin-module-resolver": "^5.0.0",
    "babel-plugin-react-native-platform-specific-extensions": "^1.1.1",
    "babel-preset-expo": "^10.0.0",
    "expo": "^50.0.0",
    "expo-application": "~5.8.3",
    "expo-asset": "~9.0.2",
    "expo-av": "~13.10.5",
    "expo-camera": "~14.0.5",
    "expo-checkbox": "~2.7.0",
    "expo-constants": "~15.4.5",
    "expo-device": "~5.9.3",
    "expo-file-system": "~16.0.7",
    "expo-font": "~11.10.3",
    "expo-image-picker": "~14.7.1",
    "expo-linking": "~6.2.2",
    "expo-location": "~16.5.5",
    "expo-notifications": "~0.27.6",
    "expo-splash-screen": "~0.26.4",
    "expo-status-bar": "~1.11.1",
    "expo-task-manager": "~11.7.2",
    "expo-updates": "~0.24.11",
    "expo-video-thumbnails": "~7.9.0",
    "firebase": "^9.6.5",
    "geolib": "^3.3.3",
    "is-color": "^1.0.2",
    "lodash": "^4.17.21",
    "moment": "^2.29.3",
    "react": "18.2.0",
    "react-hook-form": "^7.20.2",
    "react-i18next": "~10.13.1",
    "react-native": "0.73.4",
    "react-native-animation-hooks": "~1.0.1",
    "react-native-async-storage": "^0.0.1",
    "react-native-collapsible": "^1.6.0",
    "react-native-dotenv": "^3.4.8",
    "react-native-elements": "^2.0.0",
    "react-native-gesture-handler": "~2.14.0",
    "react-native-image-viewing": "^0.2.2",
    "react-native-keyboard-aware-scroll-view": "^0.9.4",
    "react-native-map-link": "^2.7.10",
    "react-native-modal": "^13.0.1",
    "react-native-modal-datetime-picker": "^14.0.1",
    "react-native-paper": "^3.10.1",
    "react-native-reanimated": "~3.6.2",
    "react-native-render-html": "^6.3.4",
    "react-native-safe-area-context": "4.8.2",
    "react-native-sass-transformer": "^2.1.1",
    "react-native-screens": "~3.29.0",
    "react-native-svg": "14.1.0",
    "react-native-vector-icons": "^9.1.0",
    "react-native-webview": "13.6.4",
    "react-pdf": "^6.2.2",
    "react-redux": "^7.2.0",
    "redux": "^4.0.5",
    "redux-persist": "^6.0.0",
    "rn-pdf-reader-js": "^4.1.1",
    "sentry-expo": "~7.2.0",
    "yarn": "^1.22.21"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/lodash": "^4.14.168",
    "@types/moment": "^2.13.0",
    "@types/qs": "^6.9.7",
    "@types/react": "~18.2.14",
    "@types/react-native-collapsible": "^0.11.0",
    "@types/react-pdf": "^6.2.0",
    "@types/react-redux": "^7.1.7",
    "husky": "~3.0.8",
    "jest": "^29.2.1",
    "lint-staged": "~9.4.2",
    "prettier": "~1.19.1",
    "sass": "^1.71.1",
    "tslint": "~5.20.1",
    "tslint-config-prettier": "~1.18.0",
    "typescript": "^5.1.3"
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "tslint --fix --project tsconfig.lint.json",
      "prettier --write",
      "git add"
    ],
    "locales/*.json": [
      "prettier --write",
      "git add"
    ]
  }
}```
kristerkari commented 6 months ago

Thanks! Yeah newest Expo version (50) is not supported yet. I'll need to have a look at fixing that.

25juan commented 6 months ago

Excuse me, have you solved it now?

kristerkari commented 5 months ago

@25juan sorry, not yet. I'll try to remember to have a look at it this weekend.

The fix itself is not that complicated, it's just that the transformer path that Expo uses is a custom one: https://github.com/kristerkari/react-native-svg-transformer/blob/master/index.js#L14

carlos-lao commented 5 months ago

I wanted to follow up on whether this has been fixed.

chRohowski commented 4 months ago

I am experiencing the same issue - is there any fix yet?

my metro.config.js might by off though (tried both sassTransformerPath and sassTransformer):

const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);

config.transformer = {
  ...config.transformer,
  babelTransformerPath: require.resolve('react-native-svg-transformer'),
  sassTransformerPath: require.resolve('react-native-sass-transformer'),
};

config.resolver = {
  ...config.resolver,
  assetExts: config.resolver.assetExts.filter((ext) => ext !== 'svg'),
  sourceExts: [...config.resolver.sourceExts, 'scss', 'sass'],
};

module.exports = config;
kristerkari commented 4 months ago

Sorry, I know that there are many that have been waiting for the update, but I haven't had time to look at it yet.

I want to cleanup the code for the transformer at the same time when adding the Expo support, which needs me to do a bit of testing using a React Native app together with the transformer. I'll try to find some time soon to fixing this.

kristerkari commented 4 months ago

This should be fixed in the newest 3.0.0 version. Please let me know if you are still facing any problems after updating.

https://github.com/kristerkari/react-native-sass-transformer/releases/tag/v3.0.0

Jankku commented 4 months ago

Hello, I haven't managed to get the latest 3.0.0 version working with Expo v50. No errors, scss styles are still an empty object.

I made a minimal demo and tested it on Android emulator: https://github.com/Jankku/expo-rnst

kristerkari commented 4 months ago

I also quickly tried the new version with Expo 50, and it just threw some weird internal error.

I have to try to figure out why it's failing. Thanks for the repro, I'll have a look at it too!

kristerkari commented 3 months ago

I had a bit deeper look into this problem, and the reason that Expo 50/51 is not working seems to be because they are doing some custom handling for CSS and Sass files for Web in their custom transformer: https://github.com/expo/expo/blob/main/packages/%40expo/metro-config/build/transform-worker/transform-worker.js#L173-L178

I have not yet found a fix, but at least I now know why it's happening.

ryskin commented 3 months ago

@kristerkari still have

iyosayi0x commented 2 months ago

@kristerkari

I was able to come up with a tempoary solution for sdk 51 & 50

Creating a custom transformer worker

I removed all the css processing that was done and ended up with this

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = void 0;
/**
 * Copyright 2023-present 650 Industries (Expo). All rights reserved.
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
const worker = __importStar(require("@expo/metro-config/build/transform-worker/metro-transform-worker"));

async function transform(config, projectRoot, filename, data, options) {
    const environment = options.customTransformOptions?.environment;
    const isClientEnvironment = environment !== 'node' && environment !== 'react-server';

    if (isClientEnvironment &&
        (filename.match(new RegExp(`^app/\\+html(\\.${options.platform})?\\.([tj]sx?|[cm]js)?$`)) ||
        filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/))) {
        return worker.transform(config, projectRoot, filename, !options.minify
            ? Buffer.from('"> The server-only file was removed from the client JS bundle by Expo CLI."')
            : Buffer.from(''), options);
    }

    if (isClientEnvironment &&
        !filename.match(/\/node_modules\//) &&
        filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/)) {
        return worker.transform(config, projectRoot, filename, Buffer.from(''), options);
    }

    return worker.transform(config, projectRoot, filename, data, options);
}

exports.transform = transform;
/**
 * A custom Metro transformer that adds support for processing Expo-specific bundler features.
 */
module.exports = {
    ...worker,
    transform,
};
//# sourceMappingURL=transform-worker.js.map

Update metro config

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname, {
  isCSSEnabled: true,
});

config.transformerPath = require.resolve('./custom-transformer')
config.transformer.babelTransformerPath = require.resolve("react-native-sass-transformer")
config.transformer.assetPlugins = ['expo-asset/tools/hashAssetFiles'];
config.resolver.assetExts = [...config.resolver.assetExts, 'ttf'];
config.resolver.sourceExts = [...config.resolver.sourceExts, 'scss', 'sass'];

module.exports = config;
kristerkari commented 2 months ago

Thanks @iyosayi0x !

Here's a slightly cleaned up version of your workaround:

transform-worker.js

const worker = require("@expo/metro-config/build/transform-worker/metro-transform-worker.js");

async function transform(config, projectRoot, filename, data, options) {
  const environment = options.customTransformOptions?.environment;
  const isClientEnvironment =
    environment !== "node" && environment !== "react-server";
  if (
    isClientEnvironment &&
    (filename.match(
      new RegExp(`^app/\\+html(\\.${options.platform})?\\.([tj]sx?|[cm]js)?$`)
    ) ||
      filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/))
  ) {
    return worker.transform(
      config,
      projectRoot,
      filename,
      !options.minify
        ? Buffer.from(
            '"> The server-only file was removed from the client JS bundle by Expo CLI."'
          )
        : Buffer.from(""),
      options
    );
  }

  if (
    isClientEnvironment &&
    !filename.match(/\/node_modules\//) &&
    filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/)
  ) {
    return worker.transform(
      config,
      projectRoot,
      filename,
      Buffer.from(""),
      options
    );
  }

  return worker.transform(config, projectRoot, filename, data, options);
}

module.exports = transform;

module.exports = {
  ...worker,
  transform,
};

metro.config.js

const { getDefaultConfig } = require("expo/metro-config");

module.exports = (() => {
  const config = getDefaultConfig(__dirname);

  const { transformer } = config;

  return {
    ...config,
    transformerPath: require.resolve("./transform-worker.js"),
    transformer: {
      ...transformer,
      babelTransformerPath: require.resolve("react-native-sass-transformer"),
    },
  };
})();