I am working on a Headless eCommerce website using Gatsby, Contentful. Shopify and Nacelle.
It works fine if I run the app using npm run dev, I can see all graphql data from Contentful.
That's what I mean is that all of graphql data can be fetched on the local side even npm run build, but it doesn't compile all of graphql data on production.
Here are some of the logs while building the gatsby app on Vercel.
The logs say that there's no errors, but I can see some of warnings from there.
Here's the graphql query, it works fine on local environment.
const HomePage = ({ data }) => {
console.log('slider in index page---', data);
...
}
export const query = graphql`
query {
homepageSlider: nacelleContent(type: { eq: "heroBanner" }, handle: { eq: "home-slider" }) {
title
remoteFields
featuredMedia {
remoteImage {
childImageSharp {
gatsbyImageData(height: 2500, placeholder: TRACED_SVG)
}
}
}
}
benefits: nacelleContent(type: { eq: "list" }, handle: { eq: "benefits" }) {
handle
title
remoteFields
}
certificates: ...
testimonial: ...
satisfacts: ...
collections: ...
}
}
If I check the console log on vercel, it says all of the data has null except collections.
But all of the data exist if I check console log on local environment.
Why does this happen? It seems like a gatsby or vercel issue.
// package.json
...
"dependencies": {
"#contentful/rich-text-from-markdown": "^15.0.0",
"#contentful/rich-text-react-renderer": "^15.0.0",
"#contentful/rich-text-types": "^15.0.0",
"#emotion/core": "^10.0.35",
"#loadable/component": "^5.14.1",
"#nacelle/client-js-sdk": "^3.1.0",
"#nacelle/gatsby-source-nacelle": "^2.10.2",
"#nacelle/react-components": "^2.10.1",
"#nacelle/react-dev-utils": "^2.10.1",
"#nacelle/react-hooks": "^2.11.2",
"#nacelle/react-recharge": "^2.11.0",
"#react-hook/media-query": "^1.1.1",
"autoprefixer": "^10.2.5",
"change-case": "^4.1.2",
"fuse.js": "^6.4.1",
"gatsby": "^2.27.0",
"gatsby-alias-imports": "^1.0.6",
"gatsby-plugin-emotion": "^4.5.0",
"gatsby-plugin-fullstory": "^3.2.0",
"gatsby-plugin-google-tagmanager": "^3.2.0",
"gatsby-plugin-image": "^1.1.2",
"gatsby-plugin-manifest": "^3.1.0",
"gatsby-plugin-postcss": "^4.1.0",
"gatsby-plugin-react-helmet": "^4.2.0",
"gatsby-plugin-scroll-reveal": "0.0.7",
"gatsby-plugin-sharp": "^2.9.0",
"gatsby-plugin-transition-link": "^1.20.5",
"gatsby-source-filesystem": "^2.6.1",
"gatsby-transformer-sharp": "^2.7.0",
"gsap": "^3.6.1",
"nuka-carousel": "^4.7.7",
"numeral": "^2.0.6",
"postcss": "^8.2.8",
"react": "^16.13.1",
"react-alice-carousel": "^2.5.1",
"react-dom": "^16.13.1",
"react-helmet": "^6.1.0"
},
"devDependencies": {
"#babel/core": "^7.11.1",
"#babel/plugin-proposal-optional-chaining": "^7.11.0",
"eslint": "^7.6.0",
"eslint-plugin-react": "^7.20.5",
"eslint-plugin-react-hooks": "^4.0.8",
"lint-staged": "^10.2.11",
"tailwindcss": "^2.0.4"
},
...
// gatsby-config.js
require('dotenv').config();
module.exports = {
plugins: [
{
resolve: '#nacelle/gatsby-source-nacelle',
options: {
nacelleSpaceId: process.env.GATSBY_NACELLE_SPACE_ID,
nacelleGraphqlToken: process.env.GATSBY_NACELLE_GRAPHQL_TOKEN,
cmsPreviewEnabled: process.env.NACELLE_PREVIEW_MODE,
contentfulPreviewSpaceId: process.env.CONTENTFUL_PREVIEW_SPACE_ID,
contentfulPreviewApiToken: process.env.CONTENTFUL_PREVIEW_API_TOKEN,
cacheDuration: 1000 * 60 * 60 * 24 // 1 day in ms
}
},
'gatsby-plugin-postcss',
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
start_url: `/`,
icon: `src/images/favicon-32.png`, // This path is relative to the root of the site.
}
},
'gatsby-transformer-sharp',
'gatsby-plugin-image',
'gatsby-plugin-emotion',
'gatsby-alias-imports',
'gatsby-plugin-react-helmet',
{
resolve: `gatsby-plugin-google-tagmanager`,
options: {
id: process.env.GOOGLE_TAG_MANAGER_ID,
includeInDevelopment: false,
},
},
`gatsby-plugin-scroll-reveal`,
// `gatsby-plugin-transition-link`
]
};
UPDATE:
Node v14.x is running on my local environment, and tried to select Node version 12 on the Vercel.
Many warning messages were disappeared, but I had no luck.
What I think that's weird is that I can see the content nodes difference between develop and build.
As you can see the images, it has created 128 content nodes while running npm run dev on the local side.
Otherwise, it has created only 100 content nodes on Vercel.
I am not sure if that is the main issue.
But basically gatsby has a rule of limitation for the content nodes?
Why does this happen? It seems like a gatsby or vercel issue.
It's hard to say with the provided details. To me, it seems that the Vercel server is facing a timeout response for some GraphQL queries/products/nodes (the warnings) so it could be a pang of shared guilt.
To debug and try to fix the issue at the same time, taking into account that the project works perfectly fine locally, I would try to match environments, starting by setting the same Node version. This will assure you that you are running the same dependency version.
Vercel allows you to configure the Node version from the backoffice or by setting a node property inside engine object in the package.json. From the docs:
Defining the node property inside engines of a package.json file
will override the selection made in the Project Settings and print a
Build Step warning if the version does not match.
In order to find out which Node.js version your Deployment is using,
run node -v in the Build Command or log the output of
process.version.
So add:
"engines": { "node": "12.x" }
Being 12.x the prompted version when running the node -v command.
Hopefully, this will fix your issue but if not, it would help you to debug better.
I have a .NET Core 3.1 with ReactJs frontend (I am using the switch bionic framework) that I am trying to run in a sub folder (subapp) on IIS. Installing the app on IIS as a website works fine. The application runs without problems. But, when installing the application as a subapp under the IIS default page, will always give a 404 on any resources that react needs. This happens since the baseUrl used by React defaults to the root url.
So far I have not found a way to change this behavior, short of adding the sub-folder name to the baseurl itself. This is not a solution for me, as this one application will be installed in at least 43 sub-folders under IIS on 1 server. I can't build and deploy 43+ apps just because the sub-folder has to be updated!
Does anybody have ANY idea how I can get the react frontend to recognize the current url (which includes the sub-folder) in use and not just default to the root url?
I have tried setting "homepage": "." and "homepage": "./" in package.json, according to the post from 'gaearon' (https://github.com/facebook/create-react-app/issues/527), but it had no affect.
My config file (default.tsx) looks like this (edited on 2021/04/09):
import Project from '../src/globals/interfaces/Project';
const isDevelopment = Object.is(process.env.NODE_ENV, 'development');
const baseUrl = isDevelopment ? 'http://localhost:5105' : '.';
const config: IConfigData<Project, {}> = {
core: {
i18n: {
defaultLocale: 'en',
},
},
project: {
baseUrl,
},
runtime: {},
};
export default config;
My package.json:
{
"name": "MyApp",
"version": "3.4.2",
"description": "My Application",
"license": "Whatever",
"author": {
"name": "Werner"
},
"config": {
"AppIcon": "./src/assets/appIcon/logo.png",
"title": "My App Title",
"devServer": {
"host": "0.0.0.0",
"port": "5170",
"https": false,
"publicPath": "/"
},
"publicPath": "",
"homepage": ".",
"functionalTestBrowsers": [
"chrome",
"firefox",
"internet explorer",
"edge"
]
},
"scripts": {
"test": "jest --no-cache",
"test:update": "jest --updateSnapshot",
"start": "webpack-dev-server --env.build=dev",
"start:4110": "webpack-dev-server --env.build=dev --env.config=4110",
"start:4120": "webpack-dev-server --env.build=dev --env.config=4120",
"start:4130": "webpack-dev-server --env.build=dev --env.config=4130",
"start:4140": "webpack-dev-server --env.build=dev --env.config=4140",
"start:4150": "webpack-dev-server --env.build=dev --env.config=4150",
"start:legacy": "webpack-dev-server --env.build=dev --env.legacy=true --env.REDUX_TOOLS=logger",
"start:swidget": "webpack-dev-server --port 7070 --env.build=dev --env.swidget=true --env.legacy=true --env.REDUX_TOOLS=logger",
"start:host": "webpack-dev-server --env.build=dev --env.legacy=true --env.exposed=true --env.REDUX_TOOLS=logger",
"build": "webpack --env.build=prod --env.verbose=false",
"build:ci": "webpack --env.build=prod --env.verbose=true --env.release=true",
"build:legacy": "webpack --env.build=prod --env.legacy=true",
"build:host": "webpack --env.build=prod --env.legacy=true --env.exposed=true",
"build:swidget": "webpack --env.build=prod --env.swidget=true --env.legacy=true",
"build:multi": "webpack --env.build=multi",
"build:test:functional": "tsc -p test/functional/tsconfig.json",
"lint": "nyr lint:eslint && nyr lint:stylelint && nyr lint:prettier",
"lint:staged": "lint-staged",
"lint:eslint": "eslint --ext ts,tsx src",
"lint:prettier": "prettier --check \"./**/*\"",
"lint:stylelint": "stylelint \"src/**/*.(css|scss)\" --syntax scss",
"lint:fix": "nyr lint:fix:eslint && nyr lint:fix:stylelint && nyr lint:fix:postcss && nyr lint:fix:prettier",
"lint:fix:prettier": "prettier --write \"./**/*\"",
"lint:fix:postcss": "postcss --config postcss.config.js --env sort-only --no-map --replace \"src/**/*.(css|scss)\"",
"lint:fix:stylelint": "stylelint \"src/**/*.(css|scss)\" --syntax scss --fix",
"lint:fix:eslint": "eslint --fix --ext ts,tsx src",
"clean": "rimraf dist && rimraf coverage",
"storybook": "cross-env build=dev start-storybook -p 9001 -c .build/storybook",
"storybook:static": "cross-env build=prod build-storybook -c .build/storybook -o dist/storybook",
"test:functional": "run-p build:test:functional test:functional:selenium && run-p --race start wdio",
"test:functional:headless": "run-p build:test:functional test:functional:selenium && run-p --race start wdio:headless",
"test:functional:selenium": "selenium-standalone install --silent",
"wdio": "wdio .build/wdio.conf.js",
"wdio:headless": "cross-env WDIO_HEADLESS=true wdio .build/wdio.conf.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"post-commit": "git update-index --again"
}
},
"repository": {
"type": "git",
"url": "Don't worry! Taken out for security!"
},
"engines": {
"node": ">=8.11.3",
"npm": ">=5.6.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"Firefox ESR",
"ie >= 11"
],
"dependencies": {
"#daimler/material-ui-comps": "0.0.4-release-84.0",
"#daimler/material-ui-theme": "0.0.9",
"#daimler/typeface-daimler-cs-web": "^1.0.0",
"#material-ui/core": "^4.11.0",
"#material-ui/icons": "^4.9.1",
"#material-ui/lab": "^4.0.0-alpha.53",
"#switch/core": "2.0.0-beta.2",
"#types/react-csv": "^1.1.1",
"#types/react-router-dom": "^5.1.3",
"#types/redux-logger": "^3.0.7",
"axios": "^0.19.0",
"core-js": "~3.2.1",
"domtokenlist-shim": "~1.2.0",
"file-saver": "^2.0.2",
"inversify": "~4.3.0",
"joi-browser": "~13.4.0",
"jwt-decode": "^2.2.0",
"material-table": "^1.69.1",
"material-ui-dropzone": "^3.2.1",
"react": "~16.9.0",
"react-csv": "^2.0.3",
"react-dom": "~16.9.0",
"react-hot-loader": "~4.12.11",
"react-promise-tracker": "^2.0.5",
"react-redux": "^7.2.0",
"react-router-dom": "^5.1.2",
"react-switch": "^5.0.1",
"react-table": "^7.5.1",
"react-transition-group-v2": "^4.3.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"regenerator-runtime": "~0.13.3",
"tslib": "~1.10.0",
"typesafe-actions": "^5.1.0",
"universal-cookie": "^4.0.3",
"whatwg-fetch": "~3.0.0"
},
"devDependencies": {
"#babel/core": "~7.5.5",
"#babel/preset-env": "~7.5.5",
"#babel/runtime": "~7.5.5",
"#hot-loader/react-dom": "~16.9.0",
"#storybook/addon-actions": "~5.1.11",
"#storybook/addon-info": "~5.1.11",
"#storybook/addon-knobs": "~5.1.11",
"#storybook/addon-links": "~5.1.11",
"#storybook/addons": "~5.1.11",
"#storybook/cli": "~5.1.11",
"#storybook/react": "~5.1.11",
"#types/enzyme": "~3.10.3",
"#types/jasmine": "~3.4.0",
"#types/jest": "~24.0.18",
"#types/jwt-decode": "^2.2.1",
"#types/node": "~12.0.0",
"#types/prop-types": "~15.7.1",
"#types/react": "~16.9.2",
"#types/react-dom": "~16.9.0",
"#types/react-redux": "^7.1.7",
"#types/react-test-renderer": "~16.9.0",
"#types/storybook__addon-actions": "~3.4.3",
"#types/storybook__addon-info": "~4.1.2",
"#types/storybook__addon-knobs": "~5.0.3",
"#types/storybook__addon-links": "~3.3.5",
"#types/storybook__react": "~4.0.2",
"#typescript-eslint/eslint-plugin": "~2.0.0",
"#typescript-eslint/parser": "~2.0.0",
"#wdio/cli": "~5.12.4",
"#wdio/dot-reporter": "~5.12.1",
"#wdio/jasmine-framework": "~5.12.1",
"#wdio/local-runner": "~5.12.4",
"#wdio/selenium-standalone-service": "~5.12.1",
"#wdio/spec-reporter": "~5.12.1",
"#wdio/sync": "~5.12.3",
"babel-loader": "~8.0.6",
"clean-webpack-plugin": "~3.0.0",
"copy-webpack-plugin": "~5.0.4",
"cross-env": "~5.2.0",
"css-loader": "~3.2.0",
"css-modules-typescript-loader": "~3.0.0",
"cssjson": "~2.1.3",
"duplicate-package-checker-webpack-plugin": "~3.0.0",
"enzyme": "~3.10.0",
"enzyme-adapter-react-16": "~1.14.0",
"enzyme-to-json": "~3.4.0",
"eslint": "~6.2.1",
"eslint-config-prettier": "~6.1.0",
"eslint-plugin-prettier": "~3.1.0",
"eslint-plugin-react": "~7.14.3",
"expose-loader": "~0.7.5",
"fork-ts-checker-webpack-plugin": "~1.5.0",
"html-webpack-multi-build-plugin": "~1.0.0",
"html-webpack-plugin": "~3.2.0",
"husky": "~3.0.4",
"identity-obj-proxy": "~3.0.0",
"imagemin-lint-staged": "~0.4.0",
"jasmine": "~3.4.0",
"jest": "~24.9.0",
"lint-staged": "~9.2.3",
"mini-css-extract-plugin": "~0.8.0",
"mock-local-storage": "~1.1.8",
"npm-run-all": "~4.1.5",
"nyr": "1.1.0",
"optimize-css-assets-webpack-plugin": "~5.0.3",
"postcss": "~7.0.17",
"postcss-cli": "~6.1.3",
"postcss-extend": "~1.0.5",
"postcss-import": "~12.0.1",
"postcss-import-sync": "~7.1.4",
"postcss-loader": "~3.0.0",
"postcss-nested": "~4.1.2",
"postcss-preset-env": "~6.7.0",
"postcss-remove-prefixes": "~1.2.0",
"postcss-sorting": "~5.0.1",
"postcss-unprefix": "~2.1.4",
"prettier": "~1.18.2",
"react-ace": "^7.0.4",
"react-docgen-typescript-loader": "~3.1.1",
"react-test-renderer": "~16.9.0",
"rimraf": "~3.0.0",
"simple-progress-webpack-plugin": "~1.1.2",
"style-loader": "~1.0.0",
"stylelint": "~10.1.0",
"stylelint-config-css-modules": "~1.4.0",
"stylelint-config-recommended": "~2.2.0",
"terser-webpack-plugin": "~1.4.1",
"ts-jest": "~24.0.2",
"ts-loader": "~6.0.4",
"typescript": "~3.5.3",
"url-loader": "~2.1.0",
"webapp-webpack-plugin": "~2.7.1",
"webpack": "~4.39.2",
"webpack-cli": "~3.3.7",
"webpack-dev-server": "~3.8.0",
"webpack-merge": "~4.2.1"
}
}
Any help will be greatly appreciated.
There are 100s of posts on the internet on how to get the ReactJS front-end, as part of an asp.net core back-end, running without problems under an IIS sub-folder. Most of them are the same. Some of them do work properly while others are just plain rubbish.
Should you want to run an application (back-end and front-end) in a sub-folder, then the only way to do it is to hard-code the base url and public path in React.
Since I am using React with typescript and the switch framework I can do it like this:
default.tsx:
const baseUrl = isDevelopment ? 'http://localhost:5105' : 'http://localhost/MySubFolderName';
Then in package.json:
"publicPath": "MySubFolderName",
After building for production the app will run without problems in an IIS sub-folder.
But my goal is to have this one app running in 40+ IIS sub-folders, many of them on the same server. And it is definitely NOT an option to build the front-end separately for each installation. Think of the maintenance nightmare!!
I therefor decided to write a script that will change the required files for me. Automatically!
The first option I tried was to set the base tag, <base href="/">, in index.html, as given as a solution by many posts on the internet. My script opened this index.html and replaced the tag to included the sub-folder name: <base href="/MySubFolderName/">.
And 'lo-and-behold', the site runs! However, great was my disappointment when 2 days later I finally figured out that this is not enough. The site runs, but the routing is broken! Irrespective of which router package you use.
So, BEWARE, just setting href in a base tag is not enough.
My final solution:
EDITED:
My first solution was to hard-code the base url and public path, but with my special name: XYXYX.
After some further thinking I decided to not hard-code anything, but leave the front-end as is. The back-end now updates the required files as needed, making sure that they are correct for either a 'port' installation or for a 'subfolder' installation.
In the back-end, in Programs.cs, create a function that will change the index.html and *app.js files depending on whether the app is installed under IIS to run on a port (ex. http://domain:port) or in a subfolder (ex. http://domain/subfolder).
appsettings.json:
"AppAddress": "http://my.domain.name/MySubFolderName",
Program.cs:
...
IConfiguration Configuration = new ConfigurationBuilder()
.SetBasePath(pathToContentRoot)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
try
{
var host = CreateHostBuilder(args).Build();
...
...
try
{
UpdateFilesForSubfolderOrPortUse(Configuration["AppAddress"], pathToContentRoot);
}
catch (Exception e)
{
Log.Fatal(e, $"Host terminated. Could not rewrite files! Error: {e.Message}");
return 1;
}
host.Run();
...
...
private static void UpdateFilesForSubfolderOrPortUse(string serverAddress, string pathToContentRoot)
{
string subFolder = ExtractSubfolderName(serverAddress);
// First extract the search-string from the *app.js file
string searchString = ExtractSearchStringFromJsFile(pathToContentRoot);
string oldSubFolder = "";
if (searchString != ".")
{
oldSubFolder = searchString.Substring(searchString.IndexOf("/", searchString.IndexOf("://") + 3) + 1);
}
Dictionary<string, string> searchAndReplaceDict = new Dictionary<string, string>
{
{ "HtmlSearchString1", "" },
{ "HtmlReplaceString1", "" },
{ "HtmlSearchString2", "" },
{ "HtmlReplaceString2", "" },
{ "JsSearchString1", "" },
{ "JsReplaceString1", "" },
{ "JsSearchString2", "" },
{ "JsReplaceString2", "" },
{ "JsSearchString3", "" },
{ "JsReplaceString3", "" }
};
if ((string.IsNullOrEmpty(subFolder) && searchString == ".") || (!string.IsNullOrEmpty(subFolder) && oldSubFolder == subFolder))
{
// No conversion is needed
return;
}
else if ((!string.IsNullOrEmpty(subFolder) && searchString == "."))
{
// Convert from Port to SubFolder
searchAndReplaceDict["HtmlSearchString1"] = "link href=\"";
searchAndReplaceDict["HtmlReplaceString1"] = $"link href=\"{subFolder}/";
searchAndReplaceDict["HtmlSearchString2"] = "src=\"";
searchAndReplaceDict["HtmlReplaceString2"] = $"src=\"{subFolder}/";
searchAndReplaceDict["JsSearchString1"] = "+\"fonts/";
searchAndReplaceDict["JsReplaceString1"] = "+\"/fonts/";
searchAndReplaceDict["JsSearchString2"] = "},i.p=\"";
searchAndReplaceDict["JsReplaceString2"] = "},i.p=\"" + subFolder;
searchAndReplaceDict["JsSearchString3"] = "\"http://localhost:5105\":\".";
searchAndReplaceDict["JsReplaceString3"] = $"\"http://localhost:5105\":\"{(serverAddress[serverAddress.Length - 1] == '/' ? serverAddress.Substring(0, serverAddress.Length - 1) : serverAddress)}";
}
else if ((!string.IsNullOrEmpty(subFolder) && searchString != "."))
{
// Convert from SubFolder to SubFolder
searchAndReplaceDict["HtmlSearchString1"] = oldSubFolder;
searchAndReplaceDict["HtmlReplaceString1"] = subFolder;
searchAndReplaceDict["JsSearchString1"] = "+\"fonts/";
searchAndReplaceDict["JsReplaceString1"] = "+\"/fonts/";
searchAndReplaceDict["JsSearchString2"] = $"\"{oldSubFolder}\"";
searchAndReplaceDict["JsReplaceString2"] = $"\"{subFolder}\"";
searchAndReplaceDict["JsSearchString3"] = $"/{oldSubFolder}\"";
searchAndReplaceDict["JsReplaceString3"] = $"/{subFolder}\"";
}
else
{
// Convert from SubFolder to Port
searchAndReplaceDict["HtmlSearchString1"] = $"{oldSubFolder}/";
searchAndReplaceDict["JsSearchString1"] = "+\"/fonts/";
searchAndReplaceDict["JsReplaceString1"] = "+\"fonts/";
searchAndReplaceDict["JsSearchString2"] = $"\"{oldSubFolder}\"";
searchAndReplaceDict["JsReplaceString2"] = "\"\"";
searchAndReplaceDict["JsSearchString3"] = searchString;
searchAndReplaceDict["JsReplaceString3"] = $".";
}
DoUpdateFiles(searchAndReplaceDict, pathToContentRoot);
}
private static string ExtractSubfolderName(string serverAddress)
{
string baseAddr = serverAddress.Substring(serverAddress.IndexOf("://") + 3);
int idx = baseAddr.IndexOf('/');
string subaddr = "";
if (idx > 0)
{
subaddr = baseAddr.Substring(idx + 1);
if (subaddr.Length > 0 && subaddr[subaddr.Length - 1] == '/')
{
subaddr = subaddr.Substring(0, subaddr.Length - 1);
}
}
return subaddr;
}
private static string ExtractSearchStringFromJsFile(string pathToContentRoot)
{
string rootfolder = $"{pathToContentRoot}\\App";
var exts = new[] { "app.js" };
IEnumerable<string> files = Directory
.EnumerateFiles(#rootfolder, "*.*", SearchOption.TopDirectoryOnly)
.Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
string jsContents = File.ReadAllText(files.First());
string searchFor = "project:{baseUrl:Object.is(\"production\",\"development\")?\"http://localhost:5105\":\"";
string tmpSearchStr = jsContents.Substring(jsContents.IndexOf(searchFor) + searchFor.Length);
if (tmpSearchStr.StartsWith(".\""))
{
// This is a Port installation
tmpSearchStr = ".";
}
else
{
// This is a SubFolder installation
tmpSearchStr = tmpSearchStr.Substring(0, tmpSearchStr.IndexOf("\"}"));
}
return tmpSearchStr;
}
private static void DoUpdateFiles(Dictionary<string, string> searchAndReplaceDict, string pathToContentRoot)
{
string rootfolder = $"{pathToContentRoot}\\App";
var exts = new[] { ".html", "app.js" };
IEnumerable<string> files = Directory
.EnumerateFiles(#rootfolder, "*.*", SearchOption.TopDirectoryOnly)
.Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
foreach (string file in files)
{
string contents = File.ReadAllText(file);
if (Path.GetExtension(#file) == ".html")
{
contents = contents.Replace(searchAndReplaceDict["HtmlSearchString1"], searchAndReplaceDict["HtmlReplaceString1"]);
if (!string.IsNullOrEmpty(searchAndReplaceDict["HtmlSearchString2"]))
{
contents = contents.Replace(searchAndReplaceDict["HtmlSearchString2"], searchAndReplaceDict["HtmlReplaceString2"]);
}
}
else if (Path.GetExtension(#file) == ".js")
{
contents = contents.Replace(searchAndReplaceDict["JsSearchString1"], searchAndReplaceDict["JsReplaceString1"]);
contents = contents.Replace(searchAndReplaceDict["JsSearchString2"], searchAndReplaceDict["JsReplaceString2"]);
contents = contents.Replace(searchAndReplaceDict["JsSearchString3"], searchAndReplaceDict["JsReplaceString3"]);
}
// Make file writable
File.SetAttributes(file, FileAttributes.Normal);
File.WriteAllText(file, contents);
}
}
Now an admin can download the release of the application and install it in an IIS sub-folder. This admin can also just copy all files from one sub-folder/port installation to another. In both instances the admin must update the config file with the correct address.
The application will now, during startup, right before it runs the application, read the 2 files and replace the required strings.
The application now runs with a 'standard' front-end for a 'port' installation and with a 'hard-coded' front-end for a 'sub-folder' installation, but one that is dynamic and updated automatically depending on the 'sub-folder name'! And I can just keep on adding new applications in sub-folders or on ports without having to rebuild every time and without having to manually update the required strings to get it running ...
Yes, some people will say this is a hack, but in SW even a hack is a great solution in the absence of anything else!
I have a project based on nextjs. Oddly enough, the HMR is not working properly for my project. Every time I make changes I have to re run the process. I have attached details of my next config and package.json:
next.config:
const withSass = require("#zeit/next-sass");
const withCSS = require("#zeit/next-css");
module.exports = withCSS(
withSass({
webpack(config, options) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: "url-loader",
options: {
limit: 100000,
},
},
});
return config;
}
})
);
package.json
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next export"
},
"dependencies": {
"#zeit/next-css": "^1.0.1",
"#zeit/next-sass": "^1.0.1",
"antd": "^3.26.8",
"chartjs": "^0.3.24",
"classnames": "^2.2.6",
"draft-js": "^0.11.4",
"isomorphic-unfetch": "^3.0.0",
"moment": "^2.24.0",
"next": "^9.2.1",
"node-sass": "^4.13.1",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-helmet": "^5.2.1",
"react-markdown": "^4.3.1",
"react-mde": "^8.1.0",
"react-redux": "^7.2.0",
"react-select": "^3.0.8",
"react-slick": "^0.25.2",
"react-toastify": "^5.5.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
"showdown": "^1.9.1",
"slick-carousel": "^1.8.1"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-react": "^7.18.3",
"url-loader": "^3.0.0"
}
I have tried removing node_modules and reinstalling again as well, doesnt seem to fix the issue.
Following is my project structure
Got help from #felixmosh. There was issue because of my folders were case was not matching route case. Fixed the issues by changing folder name to route names.
I just solved this problem by deleting folder ".next" and then closing terminal, and run npm run dev again.
Sometimes, when you accidentally name a component starting with a lowercase and then later changes to capital letter, next cache will still consider the older name and won't count it as a normal component.
The easiest solution is to copy the contents of the components, delete the file, and create one with a proper component name.
in my case hot reload didn't work because there was assetsPrefix in my next.config.js. You can remove or change them to "/" in the development mode and everything will be as expected.
For people NOT using modules and using Typescript paths.
For my setup, I just like having general .scss files
So, in my _app.tsx
I add Styles/index.scss
Then, in my index.scss
I add my #import '~Styles/flex.scss'
take note of the ~
this was required for it to work with hot reloading.
Wrt to Next js version 10+ and in addition to Pranay's answer, make sure your routes case matches the case of the folder.
Also, I noticed the component name has to start with the upper case. If your file name is dashboard.jsx, the component name should be Dashboard.
// home/dashboard.js
const Dashboard = () => {
....
}
export default Dashboard
I had to go to package.json remove react and react-dom and re add them with yarn add.
In my case, it wasn't working on Chrome. But it was working on Edge browser. And funny enough... even on a guest chrome window.
So all I had to do was...
Clear the cookies and hard refresh.
Force audit fix worked for me. Don't know the exact reason, might be some conflicts between dependencies.
Steps
1)Run this command in your next project directory.
npm audit fix --force
you need to run npm run dev instead npm run start
I use vue-cli project (that installed electron+seleniumWebDriver) run tests with ChromeDriver.
I keep running chromedriver (that installed in my vue-cli project) then I ran the test file 'node ff.js'.
The Electron app was run in screen but nothing happened. Then wait till the process exited, the result became error 'UnhandledPromiseRejectionWarning: WebDriverError: unknown error: DevToolsActivePort file doesn't exist'.
I searched the web for it and found the solution that the others claimed it works fine. It is just add a '--disable-dev-shm-usage' to chrome options. But it still error.
ff.js
const webdriver = require('selenium-webdriver')
const chrome = require('selenium-webdriver/chrome');
const driver = new webdriver.Builder()
// The "9515" is the port opened by chrome driver.
.usingServer('http://localhost:9515')
.withCapabilities({
chromeOptions: {
args: ['--headless', '--disable-gpu', '--no-sandbox', '--disable-extensions', '--disable-dev-shm-usage', 'start-minimized'],
// Here is the path to your Electron binary.
binary: './dist_electron/mac/myapp.app/Contents/MacOS/myapp'
}
})
.setChromeOptions(new chrome.Options().addArguments('--remote-debugging-port=7070'))
.forBrowser('electron')
.build()
driver.quit()
package.json
"devDependencies": {
"#types/chai": "^4.1.0",
"#types/mocha": "^5.2.4",
"#vue/cli-plugin-babel": "^3.5.0",
"#vue/cli-plugin-e2e-nightwatch": "^3.6.0",
"#vue/cli-plugin-eslint": "^3.5.0",
"#vue/cli-plugin-typescript": "^3.5.0",
"#vue/cli-plugin-unit-mocha": "^3.5.0",
"#vue/cli-service": "^3.5.0",
"#vue/eslint-config-standard": "^4.0.0",
"#vue/eslint-config-typescript": "^4.0.0",
"#vue/test-utils": "1.0.0-beta.29",
"babel-eslint": "^10.0.1",
"babel-plugin-istanbul": "^5.1.3",
"bootstrap": "^4.3.1",
"chai": "^4.1.2",
"chromedriver": "^73.0.0",
"copy-webpack-plugin": "^5.0.2",
"electron": "^4.0.0",
"electron-rebuild": "^1.8.4",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"jquery": "^3.4.0",
"mocha": "^6.1.4",
"mochawesome": "^3.1.2",
"node-sass": "^4.9.0",
"nyc": "^14.0.0",
"popper.js": "^1.15.0",
"sass-loader": "^7.1.0",
"selenium-webdriver": "^4.0.0-alpha.1",
"ts-node": "^8.1.0",
"ts-protoc-gen": "^0.9.0",
"typescript": "^3.2.1",
"vue-cli-plugin-electron-builder": "^1.2.0",
"vue-template-compiler": "^2.5.21",
"vuex-module-decorators": "^0.9.8",
"webdriverio": "^5.8.3"
}
ps. I've tried with ChromeDriver#74.x.x, it still resulted same error.
Actually, I've tried with Selenium RobotFramework and still found the same error. I think it was something wrong with my test writing or some settings. (I'm not good in test tool.)
Just for information
mytest.robot
*** Settings ***
Library Selenium2Library
Variables vars.py
*** Test Cases ***
Foohaha
Create Webdriver Remote desired_capabilities=${binary_location} command_executor=http://localhost:9515
Log To Console ${item.get_attribute('innerHTML')}
[Teardown] Close All Browsers
P.s. The reason why not spectron is the tester I have only use the Selenium.
After I tried almost recommended testing library. I ended up with testcafe. Setting the pref app path to my distributed app's binary then set the page to open as html file in test file. The primary path was a default value.
This is how I set it up.
.testcafe-electron-rc
{
"mainWindowUrl": "app://./index.html",
"electronPath": "/Users/xxx/Documents/ProjectFolder/dist_electron/mac/my-lovely-app.app/Contents/MacOS/my-lovely-app",
"openDevTools": "true"
}
package.json
"scripts": {
...
"cafe": "testcafe \"electron:/Users/xxx/Documents/ProjectFolder/.testcafe-electron-rc\" \"/Users/xxx/Documents/ProjectFolder/tests/e2e/specs/testcafe.js\""
},
"devDependencies": {
...
"selenium-webdriver": "^4.0.0-alpha.1",
"testcafe": "^1.1.4",
"testcafe-browser-provider-electron": "0.0.8"
}
testcafe.js
import { Selector } from 'testcafe';
fixture(`Index page`)
.page('/Users/xxx/Documents/ProjectFolder/dist_electron/bundled/index.html');
test('Body > Paragraph contains "Hello World!"', async testController => {
const paragraphSelector = await new Selector('#nav > a.router-link-exact-active.router-link-active');
const txt = await new Selector('#app > div.auth-page > div > div > div > form > fieldset:nth-child(1) > input');
await testController.click(paragraphSelector)
.typeText(txt, 'Peter Parker');
// .expect(paragraphSelector.innerText).eql("Login");//.eql('Login');
});
Hope this will help someone.