Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behavior with monorepo #145

Open
Sayan751 opened this issue Apr 17, 2018 · 8 comments
Open

Unexpected behavior with monorepo #145

Sayan751 opened this issue Apr 17, 2018 · 8 comments

Comments

@Sayan751
Copy link
Member

Sayan751 commented Apr 17, 2018

I'm submitting a bug report

  • Library Version:
    3.0.0-rc.1

Please tell us about your environment:

  • Operating System:
    Windows 10

  • Node Version:
    6.11.2 | 8.10.0

  • NPM Version:
    3.10.10 | 5.6.0
  • JSPM OR Webpack AND Version
    webpack 4.5.0
  • Browser:
    Chrome 65.0.3325.181 | Firefox 59.0.2 | Edge 41.16299.371.0

  • Language:
    TypeScript 2.8.1

Current behavior:
The plugin is not working as expected when packages are symlinked. To demonstrate the behavior, I have created a repository. I have a monorepo (using lerna) with following structure:

|-modules
|  |-main       // aurelia-webpack project
|  |-myplugin  // aurelia plugin as npm module
|  |-services    // an npm module containing services
|-package.json  // contains npm scripts using lerna to build app

If I want to register some module specific routes from myplugin, I need to use

{ moduleId: PLATFORM.moduleName("./views/plugin-home"), route: "myplugin" }

With this, the webpack build for main works iff I set resolve.symlinks: false in my webpack config. However, this causes another problem which portrays as if the DI is failing. To clarify, let's assume that there is a MyService exported from services package, which is used in both main/main.ts and myplugin/index.ts with Container.instance.get. Now, one would expect that same instance will be provided in both the places. However, that is not the case as MyService is being resolved from 2 different sources:

  • For main/main.ts: from main/node_modules/services/...
  • For myplugin/index.ts: from main/node_modules/myplugin/node_modules/services/... (as compiled from main)
    And this problem can only be resolved using resolve.symlinks: true, which provides unique path for both cases. One can verify using the provided repository and tweaking the configurations.

Expected/desired behavior:

  • What is the expected behavior?
    The expected behavior is that one configuration should support both the cases. Also if possible, please add documentation/best practices for monorepo.

  • What is the motivation / use case for changing the behavior?
    In a monorepo application one would ideally like to symlink the local dependencies, so that a simple watch task to build the sources, makes the changes immediately available for the running application.

@Sayan751
Copy link
Member Author

A workaround for this can be to define alias in webpack config for the shared modules (services in the above example). With that, the duplication of nested modules can be avoided, even with symlink set to false.

...
resolve: {    
    alias: {
      'services': path.resolve(__dirname, "../services"), ...
    } ...
}, ...

However, this can also be cumbersome when there are several shared modules.

@jods4
Copy link
Contributor

jods4 commented Apr 23, 2018

I've had problems with monorepo recently as well.
It can get hairy rather quickly and there are many variations. For example you did not consider what happens if you hoist your node_modules at root.

I am not sure what the best solution here is. Any idea is welcome.

Maybe I should add a mono-repo mode to the plugin?

If I try to sum up the options so far:

  • symlinks: false kind of works but has duplicated modules if they are not hoisted.
    There was a webpack plugin to dedupe modules, would that help? If modules where hoisted would that be better (can we force it)?
  • symlinks: true will surely fail because AureliaWebpackPlugin can't establish module names for things outside your project root.
    Is this the only problem? It might be fixable with clever config or maybe a slight change to the plugin.
    For example, defining aliases might help even though you don't use them.
    Try `"myplugin": "../myplugin/dist" or what is appropriate for you.

@Sayan751
Copy link
Member Author

@jods4 I indeed hoisted the dependencies to root node_modules folder, but still the local node_modules (the ones, which are in my modules folder), stays in individual node_modules folder even if I have hoisted. And I have not found (or maybe I have missed) any lerna documentation to avoid that using npm.

However, when one uses yarn (workspace) + lerna, all dependencies for all projects in a monorepo can be hoisted in the root node_modules folder. And with that the alias can be removed. You may check it here in my repository's yarn-ws branch.

And regarding the dedupe webpack plugin, I think it is long deprecated in favor of npm dedupe (which does not work in my case) and yarn workspace (I think).

@Alexander-Taran
Copy link

as far as I remember there are options in yarn workspaces to keep some of the modules.. not hoisted..
would that help?

@Sayan751
Copy link
Member Author

@Alexander-Taran With yarn workspace, it works properly. In that setup as well, one need to use symlink: false, but no alias is required.

@indfnzo
Copy link

indfnzo commented Jun 2, 2018

I might be having a similar issue.

My structure is something like this:

|- clients
|  |- common                // an aurelia plugin containing common reusable components
|  |  |- comps_dir
|  |  |- index.js
|  |- app1                  // aurelia webpack app
|  |  |- src
|  |  |  |- ...             // local resources for this app
|  |  |  |- main.ts         // calls /clients/common as a plugin/feature
|  |  |- webpack.config.js
|  |  |- package.json
|  |- app2
|  |  |- ...
|  |- app3
|  |  |- ...

from within the webpack config of each app, I'm adding the common directory as a module and alias:

resolve: {
    extensions: ['.js'],
    modules: [path.resolve(__dirname, '../common'), 'src', 'node_modules'],
    alias: {
        common: path.resolve(__dirname, '../common')
    }
},

Despite the common modules being resolved and loaded by the individual apps, the modules doesn't seem like they're getting transpiled by babel since I'm getting syntax errors:

ERROR in ../common/elements/xxx.js
Module build failed: SyntaxError: C:/Users/xxxx/Documents/Projects/xxx/clients/common/elements/xxx.js: Unexpected token (4:0)

  2 | import { ApiService } from 'services/api';
  3 |
> 4 | @inject(ApiService)
    | ^
  5 | export class UiXxx {
  6 |   constructor(api) {
  7 |           this.api = api;

My commons dir doesn't compile itself. It's literally just all the source files since I expect the apps using them to compile these along with everything else. These common modules can't go on their own builds, since Sass files inside common are tightly coupled with the architecture of each app. Am I doing something wrong here?

Btw, all the solutions at babel/babel-loader#293 doesn't seem like they work for my case.

@Sayan751
Copy link
Member Author

Sayan751 commented Jun 2, 2018

@jemhuntr I think this is a different issue.

You have not specified if you are using lerna or not. If not you may benefit from using something like lerna or yarn workspace.

I would suggest to build your common module as an npm package. If there are some app specific stuffs inside your common module, then it is not really common. If you want to style your apps and put all css in a common stylesheet, then a better alternative would be to use css modules. Here are some resources for css module:

Then you can build your common module and use lerna to add it to your apps. That way the common module will reside in node_modules of each app. And you don't have define the alias for common module unless there is a nested dependency, such as app-->common and app-->someotherModule-->common.

Hope this helps.

@indfnzo
Copy link

indfnzo commented Jun 3, 2018

@Sayan751 Yeah, I'm not using lerna nor do I have a monorepo structure. This is just the most relevant resource I found about the specific issue, and I figured out eventually that the issue is most likely with webpack upstream.

And thanks, CSS Modules is definitely the way to go. The last time I tried getting it to work didn't exactly go well though, but I might try it again on my next project.

I really just have been looking for an easy way to share local resources between multiple webpack builds, as the apps are literally just copy-pastes of each other with different implementations and content structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants