shirakaba / react-nativescript

React renderer for NativeScript
https://react-nativescript.netlify.com
MIT License
280 stars 14 forks source link

React Dev Tools cannot find variable window #63

Open heritiermwalila opened 3 years ago

heritiermwalila commented 3 years ago

file: node_modules/react-devtools-core/dist/backend.js:1:220: JS ERROR ReferenceError: Can't find variable: window (CoreFoundation) *** Terminating app due to uncaught exception 'NativeScript encountered a fatal error: ReferenceError: Can't find variable: window

`import 'nativescript-websockets'; // Import for side-effects. import * as React from "react";

Object.defineProperty(global, 'WebSocket', { value: (global as any).WebSocket });

/ Controls react-nativescript log verbosity. true: all logs; false: only error logs. / Object.defineProperty(global, 'DEV', { value: false });

const { connectToDevTools } = require('react-devtools-core');

connectToDevTools({ host: 'localhost', port: 8097, resolveRNStyle: null, isAppActive: () => true, });`

shirakaba commented 3 years ago

What version of react-nativescript is this?

Could you also show your Webpack config?

heritiermwalila commented 3 years ago

@shirakaba

"react": "^16.13.1", "react-nativescript": "^1.0.6",

`const { join, relative, resolve, sep } = require("path");

const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); const { getNoEmitOnErrorFromTSConfig } = require("nativescript-dev-webpack/utils/tsconfig-utils"); const CleanWebpackPlugin = require("clean-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); const TerserPlugin = require("terser-webpack-plugin"); const hashSalt = Date.now().toString();

module.exports = env => { // Add your custom Activities, Services and other Android app components here. const appComponents = env.appComponents || []; appComponents.push(...[ "tns-core-modules/ui/frame", "tns-core-modules/ui/frame/activity", ]);

const platform = env && (env.android && "android" || env.ios && "ios" || env.platform);
if (!platform) {
    throw new Error("You need to provide a target platform!");
}

const platforms = ["ios", "android"];
const projectRoot = __dirname;

if (env.platform) {
    platforms.push(env.platform);
}

// Default destination inside platforms/<platform>/...
const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot));

const {
    // The 'appPath' and 'appResourcesPath' values are fetched from
    // the nsconfig.json configuration file.
    appPath = "app",
    appResourcesPath = "app/App_Resources",

    // You can provide the following flags when running 'tns run android|ios'
    snapshot, // --env.snapshot
    production, // --env.production
    uglify, // --env.uglify
    report, // --env.report
    sourceMap, // --env.sourceMap
    hiddenSourceMap, // --env.hiddenSourceMap
    hmr, // --env.hmr,
    unitTesting, // --env.unitTesting,
    verbose, // --env.verbose
    snapshotInDocker, // --env.snapshotInDocker
    skipSnapshotTools, // --env.skipSnapshotTools
    compileSnapshot // --env.compileSnapshot
} = env;

const useLibs = compileSnapshot;
const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap;
const externals = nsWebpack.getConvertedExternals(env.externals);

const appFullPath = resolve(projectRoot, appPath);
const hasRootLevelScopedModules = nsWebpack.hasRootLevelScopedModules({ projectDir: projectRoot });
let coreModulesPackageName = "tns-core-modules";
const alias = env.alias || {};
alias['~'] = appFullPath;

if (hasRootLevelScopedModules) {
    coreModulesPackageName = "@nativescript/core";
    alias["tns-core-modules"] = coreModulesPackageName;
}
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);

const entryModule = nsWebpack.getEntryModule(appFullPath, platform);
const entryPath = `.${sep}${entryModule}.ts`;
const entries = env.entries || {};
entries.bundle = entryPath;

const tsConfigPath = resolve(projectRoot, "tsconfig.tns.json");

const areCoreModulesExternal = Array.isArray(env.externals) && env.externals.some(e => e.indexOf("tns-core-modules") > -1);
if (platform === "ios" && !areCoreModulesExternal) {
    entries["tns_modules/tns-core-modules/inspector_modules"] = "inspector_modules";
};

let sourceMapFilename = nsWebpack.getSourceMapFilename(hiddenSourceMap, __dirname, dist);

const itemsToClean = [`${dist}/**/*`];
if (platform === "android") {
    itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "src", "main", "assets", "snapshots")}`);
    itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "build", "configurations", "nativescript-android-snapshot")}`);
}

const noEmitOnErrorFromTSConfig = getNoEmitOnErrorFromTSConfig(tsConfigPath);

nsWebpack.processAppComponents(appComponents, platform);
const config = {
    mode: production ? "production" : "development",
    context: appFullPath,
    externals,
    watchOptions: {
        ignored: [
            appResourcesFullPath,
            // Don't watch hidden files
            "**/.*",
        ]
    },
    target: nativescriptTarget,
    entry: entries,
    output: {
        pathinfo: false,
        path: dist,
        sourceMapFilename,
        libraryTarget: "commonjs2",
        filename: "[name].js",
        globalObject: "global",
        hashSalt
    },
    resolve: {
        extensions: [".ts", ".js", ".scss", ".css"],
        // Resolve {N} system modules from tns-core-modules
        modules: [
            resolve(__dirname, `node_modules/${coreModulesPackageName}`),
            resolve(__dirname, "node_modules"),
            `node_modules/${coreModulesPackageName}`,
            "node_modules",
        ],
        alias,
        // resolve symlinks to symlinked modules
        symlinks: true
    },
    resolveLoader: {
        // don't resolve symlinks to symlinked loaders
        symlinks: false
    },
    node: {
        // Disable node shims that conflict with NativeScript
        "http": false,
        "timers": false,
        "setImmediate": false,
        "fs": "empty",
        "__dirname": false,
    },
    devtool: hiddenSourceMap ? "hidden-source-map" : (sourceMap ? "inline-source-map" : "none"),
    optimization: {
        runtimeChunk: "single",
        noEmitOnErrors: noEmitOnErrorFromTSConfig,
        splitChunks: {
            cacheGroups: {
                vendor: {
                    name: "vendor",
                    chunks: "all",
                    test: (module, chunks) => {
                        const moduleName = module.nameForCondition ? module.nameForCondition() : '';
                        return /[\\/]node_modules[\\/]/.test(moduleName) ||
                            appComponents.some(comp => comp === moduleName);

                    },
                    enforce: true,
                },
            }
        },
        minimize: !!uglify,
        minimizer: [
            new TerserPlugin({
                parallel: true,
                cache: true,
                sourceMap: isAnySourceMapEnabled,
                terserOptions: {
                    output: {
                        comments: false,
                        semicolons: !isAnySourceMapEnabled
                    },
                    compress: {
                        // The Android SBG has problems parsing the output
                        // when these options are enabled
                        'collapse_vars': platform !== "android",
                        sequences: platform !== "android",
                    }
                }
            })
        ],
    },
    module: {
        rules: [
            {
                include: join(appFullPath, entryPath),
                use: [
                    // Require all Android app components
                    platform === "android" && {
                        loader: "nativescript-dev-webpack/android-app-components-loader",
                        options: { modules: appComponents }
                    },

                    {
                        loader: "nativescript-dev-webpack/bundle-config-loader",
                        options: {
                            loadCss: !snapshot, // load the application css if in debug mode
                            unitTesting,
                            appFullPath,
                            projectRoot,
                            ignoredFiles: nsWebpack.getUserDefinedEntries(entries, platform)
                        }
                    },
                ].filter(loader => !!loader)
            },

            {
                test: /\.(ts|css|scss|html|xml)$/,
                use: "nativescript-dev-webpack/hmr/hot-loader"
            },

            { test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader" },

            {
                test: /\.css$/,
                use: "nativescript-dev-webpack/css2json-loader"
            },

            {
                test: /\.scss$/,
                use: [
                    "nativescript-dev-webpack/css2json-loader",
                    "sass-loader"
                ]
            },

            {
                test: /\.ts$/,
                use: {
                    loader: "ts-loader",
                    options: {
                        configFile: tsConfigPath,
                        // https://github.com/TypeStrong/ts-loader/blob/ea2fcf925ec158d0a536d1e766adfec6567f5fb4/README.md#faster-builds
                        // https://github.com/TypeStrong/ts-loader/blob/ea2fcf925ec158d0a536d1e766adfec6567f5fb4/README.md#hot-module-replacement
                        transpileOnly: true,
                        allowTsInNodeModules: true,
                        compilerOptions: {
                            sourceMap: isAnySourceMapEnabled,
                            declaration: false
                        }
                    },
                }
            },
        ]
    },
    plugins: [
        // Define useful constants like TNS_WEBPACK
        new webpack.DefinePlugin({
            "global.TNS_WEBPACK": "true",
            "process": "global.process",
        }),
        // Remove all files from the out dir.
        new CleanWebpackPlugin(itemsToClean, { verbose: !!verbose }),
        // Copy assets to out dir. Add your own globs as needed.
        new CopyWebpackPlugin([
            { from: { glob: "fonts/**" } },
            { from: { glob: "**/*.jpg" } },
            { from: { glob: "**/*.png" } },
        ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }),
        new nsWebpack.GenerateNativeScriptEntryPointsPlugin("bundle"),
        // For instructions on how to set up workers with webpack
        // check out https://github.com/nativescript/worker-loader
        new NativeScriptWorkerPlugin(),
        new nsWebpack.PlatformFSPlugin({
            platform,
            platforms,
        }),
        // Does IPC communication with the {N} CLI to notify events when running in watch mode.
        new nsWebpack.WatchStateLoggerPlugin(),
        // https://github.com/TypeStrong/ts-loader/blob/ea2fcf925ec158d0a536d1e766adfec6567f5fb4/README.md#faster-builds
        // https://github.com/TypeStrong/ts-loader/blob/ea2fcf925ec158d0a536d1e766adfec6567f5fb4/README.md#hot-module-replacement
        new ForkTsCheckerWebpackPlugin({
            tsconfig: tsConfigPath,
            async: false,
            useTypescriptIncrementalApi: true,
            checkSyntacticErrors: true,
            memoryLimit: 4096
        })
    ],
};

if (report) {
    // Generate report files for bundles content
    config.plugins.push(new BundleAnalyzerPlugin({
        analyzerMode: "static",
        openAnalyzer: false,
        generateStatsFile: true,
        reportFilename: resolve(projectRoot, "report", `report.html`),
        statsFilename: resolve(projectRoot, "report", `stats.json`),
    }));
}

if (snapshot) {
    config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({
        chunk: "vendor",
        requireModules: [
            "tns-core-modules/bundle-entry-points",
        ],
        projectRoot,
        webpackConfig: config,
        snapshotInDocker,
        skipSnapshotTools,
        useLibs
    }));
}

if (hmr) {
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
}

return config;

}; `

shirakaba commented 3 years ago

The last time I set up React Devtools was with this version:

"react-devtools": "^3.6.1",

Are you using a newer version of the Devtools? And incidentally does that older version work?

shirakaba commented 3 years ago

I believe that the older react-devtools (^3.6.1) may work without window. If you'd like to use the latest React Devtools though, I haven't tried that myself yet, but you could try defining an alias for window in your Webpack config as follows:

        // Define useful constants like TNS_WEBPACK
        new webpack.DefinePlugin({
            "global.TNS_WEBPACK": "true",
            "process": "global.process",
            "window": "global.global", // <- Try adding this line.
        }),

This might not work, still. There is a high possibility that the latest React Devtools uses some Window APIs that don't exist on the NativeScript global object, and there would be no trivial way to provide those. But maybe we'll get lucky.

If that doesn't work, I'd recommend falling back to "react-devtools": "^3.6.1".

heritiermwalila commented 3 years ago

@shirakaba I've downgraded to the react-devtools (^3.6.1) still not working. I've also seen a lot of warning logs on the Websoket I don't if is due to the licence as mentioned on the doc

shirakaba commented 3 years ago

@heritiermwalila Strange. Have you also tried applying the extra webpack define (where window becomes global.global), at the same time as using react-devtools v3.6.1?

shirakaba commented 3 years ago

@heritiermwalila Actually, I've found the git branch where I originally got all this working – this bit looks important. Try adding these lines to your app.ts (and in this case, do not use WebpackDefinePlugin to alias window as global.global):

https://github.com/shirakaba/react-nativescript/blob/83bcada9a6ca499b2bb4c478900acbdd4fce41b1/sample/app/app.ts#L30-L33