Closed seldo closed 6 years ago
I believe I opened an issue for mounting, or I should have otherwise. The idea is that you'll be able to supply a prefix in the next config (like /some/sub/app
), and then you can merge next apps at the proxy level.
There are a few caveats however. For example, <img src="/static/logo.png">
won't work. Maybe we should introduce this.context.next.prefix
or something like that.
cc @nkzawa
Configurable prefixes would work perfectly for our use case.
The problem of this.context.next.prefix
is the value goes wrong when you load components from other app using <Link/>
.
For example, when there are two apps which mounted to /a
and /b
.
<img src={this.context.next.prefix + '/static/logo.png'} />
becomes <img src="/a/static/logo.png" />
or <img src="/b/static/logo.png" />
depends on which mounted app the component is loaded.
Another option is a compile-time thing.
import { pathPrefix } from 'next/env'
and we can replace it with webpack
Another options is to fetch the value on .json
request.
/b/page.json?pathPrefix=1
// response
{
"component": "...",
"pathPrefix": "/b"
}
Maybe we can detect if it's an external component by comparing to own pathPrefix
and automatically add the query parameter to the request.
Cannot waiting this option.
Now I extends next/dist/server
and override defineRoutes()
.
extends NextScript
in next/document
and override render()
.
Client side I monkey path XMLHttpRequest.prototype.open
We won't be implementing it in the short term. If anyone wants to work on it, we could certainly provide feedback on suggested designs
@rauchg
There are a few caveats however. For example, won't work. Maybe we should introduce this.context.next.prefix or something like that.
Maybe by using the <base>
tag? 🤔
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
@cncolder Do you have a gist or something illustrating your overrides? We need this functionality at our site as well.
@JohnPhoto, these are the monkey-patches we've done to support this. It assumes that _document only gets rendered on the server.
In _document.js
:
import Document, { Head, Main, NextScript } from 'next/document'
import htmlescape from 'htmlescape'
import Router from 'next/router'
class PrefixedNextScript extends NextScript {
render () {
const { staticMarkup, __NEXT_DATA__ } = this.context._documentProps
let { buildId } = __NEXT_DATA__
return <div>
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
__html: `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}; module={};`
}} />}
<script dangerouslySetInnerHTML={{
__html:
`
__NEXT_DATA__.prefix = "` + this.props.prefix + `";
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
if (arguments[1].indexOf("/_next/") == 0 || arguments[1].indexOf("/__webpack_hmr") == 0) {
arguments[1] = __NEXT_DATA__.prefix + arguments[1];
}
open.apply(this, arguments);
};
` }} />
{ staticMarkup ? null : <script type='text/javascript' src={this.props.prefix + `/_next/${buildId}/commons.js`} /> }
{ staticMarkup ? null : <script type='text/javascript' src={this.props.prefix + `/_next/${buildId}/main.js`} /> }
</div>
}
}
const ROOT_URL = (process.env.ROOT_URL ? process.env.ROOT_URL : "");
export default class MyDocument extends Document {
render () {
return (
<html>
<Head>
<title>Title Here</title>
<link rel="stylesheet" type="text/css" href={ROOT_URL + "/static/main.css"} />
</Head>
<body>
<Main />
<PrefixedNextScript prefix={ROOT_URL} />
</body>
</html>
)
}
}
Router.ready(() => {
if (!Router.router.change_monkeypatched) {
let router_change = Router.router.change;
Router.router.change = function(a, b, c) {
arguments[1] = "" + arguments[1]
arguments[2] = __NEXT_DATA__.prefix + arguments[2]
return router_change.apply(this, arguments);
}
Router.router.change_monkeypatched = true;
}
});
Hope this helps.
Above solution is not working in version 2.1.1
Here is the replacement which working in next@2.1.1
:
import Document, { Head, Main, NextScript } from 'next/document';
class PrefixedNextScript extends NextScript {
render() {
const { __NEXT_DATA__ } = this.context._documentProps;
const { prefix } = this.props;
const env = process.env.NODE_ENV;
// script element contain development js
let scriptElements = (
<div>
<script type="text/javascript" src={`${prefix}/_next/-/manifest.js`} />
<script type="text/javascript" src={`${prefix}/_next/-/commons.js`} />
<script type="text/javascript" src={`${prefix}/_next/-/main.js`} />
</div>
);
if (env === 'production') {
const { buildStats } = __NEXT_DATA__;
const app = 'app.js';
// script element contain production js
scriptElements =
<script
type="text/javascript"
src={`${prefix}/_next/${buildStats[app].hash}/${app}`}
/>
}
return (
<div>
<script
dangerouslySetInnerHTML={
{ __html: `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}; module={};` }
}
/>
{scriptElements}
</div>
);
}
}
Here is how to use it
<PrefixedNextScript prefix={config.prefixResource} />
this script will replace
<NextScript />
in _document.js
This issue should be close, 2.2.0
release assetPrefix
config flag.
@tpai does the assetPrefix fix this issue. I like to learn more about it. (Anyway, this was not our intention, it this fixes that would be awesome)
Hi @arunoda.
assetPrefix
does not fix the issues. It only give a prefix to resources in static dir. If it can support bundle js. If so is going to be great.
For example, when run command to build in production mode. The assetPrefix
does not include the prefix to that script.
@nndung179 assetPrefix
does support bundle js, you could check this server/document.js:46, and REAMDE also mentioned.
@arunoda Not perfectly fixed, but it still work.
next.config.js
module.exports = {
assetPrefix: '/prefix/'
};
customServer.js
express.use((req, res) => {
req.url = req.url.replace('/prefix', '');
handle(req, res);
});
I had a PR to fix this on the frontend side as well so you wouldn't need custom server logic for this: https://github.com/zeit/next.js/pull/2002
That's indeed a big issue. Since Next.js is provided as a provided as a public framework for hosting server rendered React pages in an easy way, it should be possible also to host the solution on a subdomain. We decided to use Next.js for our next admin-webapp, but this will be hosted under a subdomain like www.some-domain.com/admin and this would actually not possible or am I wrong?
@janbaer it actually possible. I have met the same problem like you. I used the solution above to solve. But I believe in the future it will be solved
If anyone else is trying to run next on a sub-route with express, I had to use this express route to re-write the HMR pings:
app.use('/_next/*', (req, res) => {
const newURL = req.originalUrl.replace('_next', 'admin/_next');
res.redirect(newURL);
});
Thanks for that solution @wesbos 👌 Pretty sure it'll help people 👍
@nndung179 @wesbos Thanks for your solutions, I'll try it. But I think it should be a feature of the framework, like the feature with for the CDN support.
I tried to use the server side solutions from @wesbos and @tpai and it worked for me locally somehow. But in case it would run on a server with Nginx as proxy-server which is only the /admin requests proxy to my Next.js page it wouldn't work, since the request to /_next wouldn't arrive in my server.js.
This is actually a make or break issue if Next.js is to be used in a real production environment. @tscanlin's fix is only two lines long. Is there a way I/we can help get his fix in right now or at least formulate a temporary workaround?
@manolkalinov glad to hear my fix is working for you! Could you comment on the PR I opened to help make a case for getting this change landed? I'll add a comment too. https://github.com/zeit/next.js/pull/2002
Finding issues with my method - the webpack HMR breaks and they live-reload takes 1-2 seconds instead of being instant. I guess because you can't proxy the eventsouce through express.
I'm also trying to make next.js app work from subfolder. I add appPrefix
in config, and now all _next/*
links in Githubissues.
Next uses routes like
/_next
and/static
by default. If you wanted to run multiple different Next apps on the same domain, they would collide with each other. Can these paths be made configurable?