Stencil version:
@stencil/[email protected]
I'm submitting a:
[x] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or https://stencil-worldwide.slack.com
Current behavior:
I want to build a web app that host on a sub path such as Github Project Page.
I set the publicPath: "build" in the stencil.config.js , but get a 404 error in the browser console.
It try to load http://localhost:3333/build/app.tv6pldgw.js.
Expected behavior:
The correct path is http://localhost:3333/build/app/app.tv6pldgw.js
Steps to reproduce:
Related code:
exports.config = {
publicPath: "build",
serviceWorker: false
};
Other information:
What is the correct way to build a stencil web app which hosted on a subpath?
what happens if you set the public path to build/app ?
If I set the publicPath to build or /build, it will load the wrong path http://localhost:3333/build/app.xxx.js.
If I set the publicPath to build/app, no error will appear in the browser console. But some components javascript files will not load.

It seems like the /build/app is the default value which behave as expected.

But I am trying to build a web app which hosted on a subpath. It is helpful if I can remove the first slash of the path.
I've looked into this pretty in depth and I have a few things to add. My use case is to have my web components in a subpath on the server (https://patterns.boston.gov/web-components/all.js) and be able to load those components from other domains.
Additionally, I'm somewhat interested in being able to do prerendering on this server.
Here are some things I've found:
Setting any publicPath at all keeps components from being able to be loaded from another domain. Having a public path disables discoverPublicPath and causes additional component scripts to be loaded from the page鈥檚 domain, not the loader鈥檚 domain.
What I think the OP is seeing mostly is that publicPath is also assumed to include the namespace path component as well.
When the loader script is inlined as part of prerendering, it assumes that wwwDir is the equivalent of / on the server. If you set wwwDir to match / (setting it to "public" for example) you can use buildDir to put the component JS in a subdirectory.
If you set wwwDir to a directory like public you probably want to set emptyWWW to false in your config to prevent Stencil from deleting your other files (which are the reason you want the components in a subdir to begin with).
The downside is that Stencil will write things into wwwDir that are not nicely compartmentalized into your subdirectory. Due to #563 you get an index.html file, and you also get the host.config.json file.
I think there's work to be done to nail down more precisely what the config directories and paths are each for, what they're relative to, _&c_.
I'd definitely like to see the use case of having things in a sub directory work correctly.
you can also set the publicPath to a full url like http://localhost:3333/build/app you'd obviously need to change this url for dev/production but this works fine for my use case.
@finneganh:
Setting any publicPath at all keeps components from being able to be loaded from another domain. Having a public path disables discoverPublicPath and causes additional component scripts to be loaded from the page鈥檚 domain, not the loader鈥檚 domain.
AFAIK this is intended and related to https://github.com/ionic-team/stencil/issues/464
Interesting. Yeah, it looks like I misunderstood it from the start. I wish it were clearer / more explicitly supported to have Stencil build entirely in a subpath of my server, though, since wwwDir pretty definitely has to point to / if you're not using publicPath.
It seems like that if the app.js is inlined into the html file, component javascript files will fail to load.
It only happened in the prerender process.(@stencil/[email protected])
But now, the latest stencil (0.6.18) will inline the app.js as default option.
Ok, let's solve this! Thank you for your patience. The hard part here are the many different scenarios and use-cases, and what we've built so far doesn't take them all into account. So if I can get some help trying to list out all of our use-cases, we can update the config and write the unit tests to make sure they're all covered. I'll start working on organizing the scenarios...
file://location: file://index.html
config: {
namespace: "App",
fsNamespace: "app"
}
<html>
<head>
<script src="build/app.js"></script>
</head>
<body>
<my-app></my-app>
</body>
</html>
location: http://mydomain.com/
config: {
namespace: "App",
fsNamespace: "app"
}
<html>
<body>
<my-cmp><prerendered-html></prerendered-html></my-cmp>
<script>/* inlined loader script */</script>
</body>
</html>
location: http://mydomain.com/
config: {
namespace: "App",
fsNamespace: "app"
}
<html>
<head>
<script>/* inlined loader script */</script>
</head>
<body>
<my-cmp></my-cmp>
</body>
</html>
location: http://mydomain.com/
config: {
namespace: "App",
fsNamespace: "app"
publicPath: 'some/other/path/app/'
}
<html>
<head>
<script src="/some/other/path/app.js"></script>
</head>
<body>
<my-cmp></my-cmp>
</body>
</html>
Request build files from:
http://mydomain.com/some/other/path/app/
main.js. It must be able to figure out it's current location.location: http://mydomain.com/
config: {
namespace: "App",
fsNamespace: "app"
}
<html>
<head>
<script src="/build/main.js"></script>
</head>
<body>
<my-cmp></my-cmp>
</body>
</html>
Request build files from:
http://mydomain.com/build/app/
location: http://mydomain.com/
config: {
namespace: "App",
fsNamespace: "app"
}
<html>
<head>
<script src="http://someotherdomain.com/build/main.js"></script>
</head>
<body>
<my-cmp></my-cmp>
</body>
</html>
Request build files from:
http://someotherdomain.com/build/app/
Here is another possible use case:
location: http://mydomain.com/some/other/path/index.html
<html>
<head>
<script src="build/main.js"></script>
</head>
<body>
<my-cmp></my-cmp>
</body>
</html>
Request build files from:
http://mydomain.com/some/other/path/build
The benefit of this approach is that you can run the app from anywhere.
For example, if you want to be able to run any version of the app from a testing server:
// To test the current production version of the app
http://test.mydomain.com/test/prod
// To test a stage build of the app
http://test.mydomain.com/test/stage
// To test an old version of the app (e.g. to diagnose a user problem)
http://test.mydomain.com/test/1.0.2/
During deployment you simply copy the files into the versioned directory and it will run without the rooted path needing to be built into the app.
run from electron
probably as same as running from file://
run from chrome extension.
popup page or options page.
Ok, so an internal update we'll do is that if publicPath is set, then it will ALWAYS use that without any modifications (it does ensure it ends with / though). Next, if publicPath is not set, we do not give it a default value. This way we know if we should always use the value or try to dynamically figure out the path.
Also, when prerendering, we can add a data attribute to the inlined script of where the runtime should find the scripts, such as <script data-path="/build/app/"> for the same reasons.
Also, would it make more sense if it was called scriptPath? Best way to say it is that this value is the url where all the lazy loaded scripts can be found.
Is it just related to scripts or is related to other assets as well? Maybe it is a different setting but it seems like you need some root for all resources.
resourcePath?
resourcesPath?
componentResourcesPath?
I'm leaning toward resourcePath
Pretty good. Maybe publishedPath?. I like resourcePath too.
Ok the tests are pretty complicated, but they're simulating the window executing the loader and core scripts for our various scenarios: https://github.com/ionic-team/stencil/blob/master/src/compiler/build/test/resource-path-www.spec.ts
The refactor involves renaming publicPath. In most cases it's a config that won't need to be changed. However, if it does need to be customized it can be done for each output target (such as www or distribution).
config.public = '/some/custom/path/';
to
config.outputTargets = [
{ type: 'www', resourcePath: '/some/custom/path/' }
];
Another difference is that if a custom resourcePath is given, it'll always use it exactly as given. If a resourcePath isn't provided, then it uses the default buildPath plus the namespace, such as /build/app.
How to build app and use relative paths as @cjorasch said?
the following config doesn't work.
<script src="build/app.js"></script>
config.outputTargets = [
{ type: 'www', resourcePath: 'build/' }
];
or keep config.outputTargets not set.
<script src="build/app.js" data-resource-path="build"></script>
<script src="build/app.js" data-resource-path="./build"></script>
You should be able to not set it all. I think resourcePath is used if there is a specific location.
Sorry, I am confused.
Let me clarify what I mean.
I want to build a web page called pageA.All the web files are assumed in one root folder.
If I want to access it through http://example.com/some/path/ , I just copy all the files to /some/path/ and keep the files structure.
Take the React as an example. I use React with Webpack. I usually bundle all the source files into one single output file. It is easy for me to host it anywhere. I set the script path to a relative path and all things are done.
Considering stencil uses lazy loading, It is more complex for me.
Is it possible to load all the source files (such as style, js script, etc) from relative path?
Or if I can run it from file:// (just open index.html), it must works anywhere.
I cannot find the right way to config it now.
Thanks for your patience and time.
Yes, it's largely the same as it was before. The lazy loaded files can be relative to your main bundle, or you can set a specific absolute path.
Also these changes have not been published yet, we'll have more info when it is. Thanks
why not
const url = "./" + bundleId + ((useScopedCss(domApi.$supportsShadowDom, cmpMeta) ? '.sc' : '') + '.js');
or
const url = "../" + namespace + "/" + bundleId + ((useScopedCss(domApi.$supportsShadowDom, cmpMeta) ? '.sc' : '') + '.js');
Most helpful comment
Interesting. Yeah, it looks like I misunderstood it from the start. I wish it were clearer / more explicitly supported to have Stencil build entirely in a subpath of my server, though, since
wwwDirpretty definitely has to point to/if you're not usingpublicPath.