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

Sharing code between multiple websites #1706

Closed
ghost opened this issue Apr 13, 2017 · 9 comments
Closed

Sharing code between multiple websites #1706

ghost opened this issue Apr 13, 2017 · 9 comments

Comments

@ghost
Copy link

ghost commented Apr 13, 2017

I'm working on 2 websites that share some code. The 2 websites are using Next and all the code including the shared code must be transpiled and for the client also bundled by webpack. I was wondering what would be the simplest way to do so.

The solutions I can think of are:

  1. The shared code should be in its own npm package and transpile itself in prepublish. Then in each website we can install the shared code package using npm's relative path (something like file:../shared-lib). But then after each change in the shared code we have to npm install it again. I'm not sure HMR would work either.
  2. Do as above, but don't transpile on prepublish. Instead instruct babel/webpack to make an exception for node_modules/shared-lib and transpile it. Using npm link would probably solve some of the hassles mentioned above. But Next doesn't seem to support this Add support to transpile modules inside node_modules  #706
  3. Use git submodules. This should work with Next seamlessly. But then instead of having 1 git repo we'll need 1 repo for the shared code and 1 repo per each website. Also need to manually init the submodules, commit, push, pull, update etc.
  4. Use symlinks. This I think should be the ideal solution, but I can't get it to work with Next. Here's what I have so far:
.git
website1/
    pages/
        index.js
    shared-lib -> ../shared-lib
    package.json
website2/
    pages/
        index.js
    shared-lib -> ../shared-lib
    package.json
shared-lib/
    lib.js

where website1/pages/index.js is:

import testfn from '../shared-lib/lib.js'
import React from 'react'

testfn(1).then(x => console.log(x));

export default () => <div>Hi</div>;

and shared-lib/lib.js is:

export default function testfn(x) {
  return Promise.resolve(x);
}

With this, I can npm run build and run the server but once I open the page I get this on the server:

Error: Cannot find module '../shared-lib/test-lib.js'

And if I use some es7 features like async function in shared-lib/lib.js, then npm run build fails with:

Error: commons.js from UglifyJs Unexpected token: keyword (function)

So clearly the shared code is not being transpiled. Is there a change I can apply in next.config.js to fixed this? or do you perhaps recommend a different solution than symlinks?

EDIT: possibly related: Tips for local developing of React modules with babel and webpack

@arunoda
Copy link
Contributor

arunoda commented Apr 13, 2017

We tried to transpile inside Next.js with this PR: #1095
But it's not complete and we need to do some research.

So, the current option is to use option 1 and use npm link to link components.

@arunoda arunoda closed this as completed Apr 13, 2017
@ghost
Copy link
Author

ghost commented Apr 13, 2017

Thanks for your response.

As you can imagine option 1 is too much trouble and completely disrupts the development flow.

If anybody else has the same problem, here's my temporary solution, until option 4 (using simple symlinks) is supported or a better solution comes along:

.git
website1/
    pages/
        index.js
    shared-lib
        lib.js
    package.json
website2/
    pages/
        index.js
    shared-lib
        lib.js
    package.json

The shared-libs are not links, instead exact copies of each other which must be kept in sync automatically during development using for example unison:

unison website1/shared-lib/ website2/shared-lib/ -auto -repeat watch -ignore 'Name .DS_STORE'

Not a huge fan of this solution, but works better than other options. Please let me know if you have any other suggestions.

@sedubois
Copy link
Contributor

I was wondering the exact same thing yesterday. Symlink of shared _document.js, next.config.js and static seems to work, but not regular .js files.

However, it looks like hard links work. @sean-shirazi have you tried that?

Anyway from a developer experience point of view, it should just be possible to import '../../shared' and that's it.

@ghost
Copy link
Author

ghost commented Apr 13, 2017

Yeah actually I thought of using hard links just to find out that we cannot hard link a directory. Hard links are only for files. Also if we commit hard links to git we get multiple copies instead of actual links.

Anyway from a developer experience point of view, it should just be possible to import '../../shared' and that's it.

Yeah, I agree. Symlinks might help keep things organized though.

@mattapperson
Copy link

@sean-shirazi here is another option...

const path = require("path");
const webpack = require("webpack");
const moduleAlias = require("module-alias");

const paths = [
    { name: "@react-joi-forms", path: path.resolve(__dirname, "../src") }
];

paths.forEach(module => {
    moduleAlias.addAlias(module.name, module.path);
});

module.exports = {
    webpack: (config, { dev }) => {
        config.resolve.alias = config.resolve.alias || {};

        paths.forEach(module => {
            config.resolve.alias[module.name] = module.path;

            // realign for hot-loader
            if (dev) {
                config.module.rules[1].include = [module.path];
                config.module.rules[3].include.push(module.path);
                config.module.rules[5].include.push(module.path);
            } else {
                config.module.rules[1].include.push(module.path);
                config.module.rules[3].include.push(module.path);
            }
        });

        return config;
    }
};

where paths is an array of the additional directories you want to include

@mattapperson
Copy link

@arunoda ˆˆ

@ghost
Copy link
Author

ghost commented Apr 15, 2017

Interesting, thanks. Though I never ran the client code bundled by webpack because the server code always crashed. I think some extra babel configuration is necessary too. Also the files must be watched for HMR.

@mattapperson
Copy link

Yes this does watch for hmr but you are correct that extra babel work is needed

@AlexGilleran
Copy link

@sean-shirazi's unison solution is the most bearable I've found... feels a bit dirty but actually works great. Because unison is two-way you can't even accidentally edit the wrong file.

You can even use concurrently to make it work together with HMR:

"scripts": {
  "build": "unison ../common common && NODE_ENV=production next build",
  "dev": "concurrently \"unison -auto -repeat watch ../common common\" \"next\"",
}

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

No branches or pull requests

4 participants