Closed quicksnap closed 1 year ago
Another thought is that I am using bs.raw
since these require
statements produce side-effects and shouldn't be optimized out.
This is an issue we have with https://github.com/reasonml/reason-tools as well: https://github.com/reasonml/reason-tools/blob/master/src/extension/popup/PopupCommon.re#L7 (Also refmt
is terrible at formatting %bs.raw
s, but that's another matter).
This is particularly annoying when refactoring and moving files around, it's very easy to lose track of these relative paths. But I think the real solution here might be better on the webpack side of things, maybe a custom loader?
Webpack can be given additional module resolve directories for files: https://webpack.js.org/configuration/resolve/#resolve-modules
However, webpack will have no way of knowing the mapping of the bsb-output.js file to its source file location.
Seems ripe for preprocessing to take care of it, and inject an absolute or relative path in replace of a token or something?
Actually, I'm going to review the Resolve and other webpack docs sometime tomorrow. There might be a way to fix this just with webpack and some config.
Other than some syntax sugar, I don't think there's anything bsc/bsb can do that webpack shouldn't be able to do, and ought to do if possible.
Webpack 1.x First, use this config
module.exports = {
...
resolve: {
modulesDirectories: [
path.resolve(__dirname, "node_modules"),
path.resolve(__dirname, "src")
]
},
module: {
loaders: [
{ test: /\.css$/, loader: "style-loader!css-loader" }
]
},
...
};
and write [%bs.raw {|require('app.css')|}];
in reason file.
I try to use fusebox but encounter the same problem, still finding the solution, maybe it will need to open issue to fusebox.
This maybe relevant to #792
What suggestions do you have in mind?
Note that raw
will never be optimized since we know nothing about it.
A few options come to mind:
bsb
option to configure js output directoryIf we allow the js output to be the same as src
, then the output files will reside in the same directory as the source. AFAIK, this is how typescript projects work.
I do not like this approach personally, as it clutters my source directories with artifacts, but it does solve this class of problem.
Solve it at the webpack layer. When we encounter a relative path, map the current path (lib/js/src/yaddayadda
) back to source directory.
Drawback: the data we're seeking is encoded in the directory structure. If bsb
alters how directory output works, this approach will break.
I'm pretty naive even to how ppx syntax works, so this is psuedo-code:
let styles : Js.t 'a = [%bs.rawReplace "require("url-loader?mimetype=image/png!%%__DIRNAME%%./file.png");"];
Drawback: Ugly semantics. Probably brittle.
@yhsiang Re: modulesDirectories
This is what I'm doing, but it doesn't allow for relative paths. So, When I move a component, I still need to update the paths. It's basically equivalent to what @glennsl is doing in reason-tools with long-ass relative paths.
modulesDirectories
just setup a directory to module source, its original design is to prevent you use long relative paths. it makes webpack to find files from your module directory like node_modules
or src
.
src/app.css => require('app.css')
src/components/header/header.css => require('components/header/header.css')
I found fusebox allow you to use relative paths like this ~/../src/app.css
(~ means homeDir which is setup for lib). First build will not work . But it works when you update some .re
file include [%bs.raw 'require('~/..src/app.css')]
. it will trigger many times update and seems bug.
we can have something like [%webpack.require "path/relative/to/package/xx.jpeg"]
, my only concern is that it would be abused.
would this work with webpack?
import * as styles from './xx.css'
@bobzhang that import statement would work.
It's worth remembering that webpack require
statements can be also be in an inline loader format:
require('url-loader?mimetype=image/png!./rel/path/to/file.png')
These are useful in practice for one-off overrides of normal webpack config
Can PPX take multiple arguments? [%webpack.require "prefix info" "rel/path/file.png"]
@quicksnap It can, prefix-info
works with es6 module? I would like to support webpack workflow, but we should mark it clearly since it is mostly a hack so that we can eventually get rid of it in the remote future. About the suffix, it is good to limit it to png
, jpg
, and css
?
In webpack workflow, usually have these extensions,
For fonts: woff
, woff2
, eot
, ttf
For images: svg
, png
, jpg
For css: css
, less
, sass|scss
@bobzhang prefix-info
would be optional, and is webpack-only syntax. Also, it's not used very much, in favor of webpack.config loader rules, so it'll usually be empty.
Feels quite hacky.. =\
@quicksnap yeah, it is very hackish, I am thinking of maybe you can keep these hacks in webpack land by using a plugin, for example
[%raw {|require ('__root_path___/path/to/file.png'|} ]
Suppose the webpack plugin could understand the meaning of __root_path__
?
@bobzhang Main issue is that __root_path__
is relative to the source file. That data is structured in the bucklescript output directory structure.
Does bucklescript plan on changing the fact that output directory structure mirrors input structure? A webpack plugin is totally possible, but it depends on having the directory structure mapping.
@quicksnap Maybe I misunderstood, here webpack require
is loading some resources (not output JS files), right?
So as long as the resource does not change its relative location to package.json
, then it is good, no?
assume
package.json
|---images
|--- a.png
then
[%raw {| require("__root_path__/images/a.png")|}
@bobzhang For example, consider a normal Webpack/Babel/JS project:
package.json
|---src
|---MyButton
|--- MyButton.js
|--- icon.png
// MyButton.js
let img = require('!url-loader?mimetype=image/png!./icon.png');
// OR, more normally:
// require('./icon.png');
// ...
In this setup, I can relocate src/MyButton/**
to somewhere else (src/components/MyButton
) and not modify the require
statement.
Using __root_path__
requires using the absolute directory structure of the project, e.g.
require(__ROOT_PATH__ + './src/components/MyButton/icon.png');
Does that clarify? A webpack plugin can accomplish this, but as mentioned, I would need to walk up the directory structure of lib/js/src
and collect the directory names to connect back to the source dir.
@quicksnap yeah, I now understand, I kinda using the absolute directory structure
is better, or maybe you can put all images in a single directory. It will get more complex here since we support multiple module formats, not just commonjs, but also es6, amdjs, google module, it would be lib/js/es6
, lib/js/amdjs
etc. If you have absolute path, it will work with all module format
Kinda related: https://github.com/rrdelaney/bs-loader/issues/4
Thank you for outlining the options, @quicksnap. I feel you, having so many auto-generated files in my src folder is weird to me also. Subscribing to this issue waiting for a future elegant solution as well.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Mentioned this in Discord this evening, figured I'd put it here instead.
I'm using
bsb
with webpack (via rehydrate-example), but I've added in css-loader/style-loader.A common pattern with React+webpack is to bundle assets (css, images, json, etc) along with their component files and relative
require
them, having weback processes it:require('./image.jpg')
. It results in a nice self-contained component that is easy to move around.Reason code such as
let styles: Js.t 'a = [%bs.raw "require('./styles.css')"];
doesn't work as webpack will be searching in the build output folder (lib/src/filename.js
) instead ofsrc
.Are there any existing solutions to make the source directory path available to BSB output?