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

Make vue scaffolding portable #4714

Open
davydnorris opened this issue Oct 18, 2019 · 9 comments
Open

Make vue scaffolding portable #4714

davydnorris opened this issue Oct 18, 2019 · 9 comments

Comments

@davydnorris
Copy link

What problem does this feature solve?

Not sure whether to log this as a bug or a feature so I went with the least offensive.

I have just decided to standardise on Vue as my front end, and am migrating my apps to use Vue and Vuetify (although looking at both Bootstrap and Carbon but that's not important). I just spent a very frustrating day trying to get a basic client/server scaffold working and am about ready to give up - performing the same task with Angular or React is so simple, but Vue has stumped me and I'm not sure it's possible.

I want the freedom to create applications that are a single deployment client/server, or that have separate processes serving client and server. Vue seems quite well designed for the latter, but is not happy when you try a single instance.

In the current case I created my Vue project in a directory under my root called './client' and I changed the package.json build to output the results of a Vue build into a top level './public' directory (I also left it alone and let it generate to './client/dist' with same results). I then set up an express server in a directory called './server' and defined a static route to './public'. Finally I moved node_modules and package.json up to the root, and adjusted the Vue build so that it built from the root.

When I ran the app, I found that the build created an empty node_modules directory under './client', but appeared to work, until I hit the main page and then I found all the Vuetify styling and CSS was missing - digging further and it appears that the build process expects node_modules to be directly inside the project and doesn't like it to be common.

What does the proposed API look like?

From an end user experience, I want the ability to have a correctly configured Vue front end living in a subdirectory of a top level project with any added plugins - I personally would be very happy with the existing scaffolding naming if it could be under its own subdirectory, but with the package.json and node_modules living somewhere else as a common asset.

I've seen existing feature requests (see #4584) that want fine-grained control over the names and locations of the individual directories - I just want control over one so that I can layout the fullstack properly.

As an added bonus, being able to reconfigure the app to have separate client and server projects by simply relocating package.json and re-running npm install would be great

@davydnorris
Copy link
Author

davydnorris commented Oct 18, 2019

Looking through some of the other submissions, such as #4499, #4411 and they face similar portability issues:

  • if you create a top level project dir, then use vue-cli to create the client sub-project within that dir, you then need to move package.json up to the top level
  • I then adjusted the contents to reflect the project name, and changed the scripts to cd into 'client' before building
  • the build broke, and I found I had to also move the various *.config.js files up to the top level as well. This would affect Support multiple projects in subfolders #4499
  • after that the build seemed to be OK, however viewing the pages they had lost all the vuetify styles. I also found an empty node_modules dir under 'client'. Digging further it appears the build process assumes node_modules is under 'client' and so nothing was included and no errors or warnings were issued. Copying node_modules back under 'client' fixed the build (I haven't tested whether it needs to be there for runtime)
  • I also found that vue ui and vue-cli were also broken by the move (sort of expected). if you fire them up in 'client' they can't find package.json and weird things happen. If you fire them up at the top level, you can import/register the project again in the UI but nothing gets added correctly, so you have to make sure you don't forget anything in setting up.

Being able to define a single top level package.json and node_modules, and a vue project directory relative to that (with a different name to the one in package.json), where the *.config.js files live would then solve all three of these issues. Maybe the project could be defined by where the vue.config.js file is and not where package.json is?

@LinusBorg
Copy link
Member

LinusBorg commented Oct 22, 2019

Hey, sorry to hear you're having trouble and thanks for the in-depth writeup.

However I still have some issues picturing what you are describing here. Would you mind putting up the skeleton of the setup you are describing? It would be very helpful to make sure that we are talking about the same things.

From what I think I do understand, you don't want to have the whole Vue CLI project in a subdirectory, with its dependencies and config. But I still struggle to envision what the intended alternative is.

@davydnorris
Copy link
Author

davydnorris commented Oct 23, 2019

I have to say - the more I play with Vue, the more I love it! Such an elegant framework - really lovely to use.

So from the top...

The app I am currently working on is a standard cloud app hosted in IBMCloud and deployed using CloudFoundry. The CD toolchain expects a single source code repo and knows it's a node.js system of some sort - it does some sizing magic with info in some extra files, but essentially it's going to look for a package.json file in the top level directory of the repo, and will do npm install to kick things off, followed by a user defined build command (usually npm run build or a shell script of some sort) to create the deploy structure inside the CloudFoundry buildpack droplet, and then npm start to get an instance running. All configuration in a cloud buildpack instance is handled by environment variables.

I create my app using the following commands (I always create the client first because that sets up the node environment):

mkdir myfullstackapp
cd myfullstackapp

vue create -n client
cd client
vue add vuetify
mv package.* ..
mv -r node_modules ..

--- client side is supposedly set up now ---

cd ..
npm i -s express
mkdir server
cd server
touch server.js
--- most basic server side is set up now ---

So now I have a top level directory containing package.json, package.lock.json and ./node_modules, and I have a ./server directory which will contain my node/express back end, and a ./client directory which contains my Vue front end. I would never have two separate client front ends (I would have them all in one client), but some folks may have, for example, a client and an admin-client, which would both be created in the same way and then you'd need to merge the package.json and node_modules from each before moving them up.

I then edit package.json and add the start command which in my case is node ./server/server.js. In the server app somewhere I have the line router.use(express.static(process.cwd() + '/client/dist')) so that it serves up the built Vue front end.

This is where the fun starts with the front end and Vue. At this point all that's in ./client is the Vuetify enabled HelloWorld so let's try to build or run it:

There's no point running anything directly from inside ./client because node_modules and package.json aren't there

If I try to run any npm run commands in the top level directory they fail in various ways:

  • vue-cli-service build fails because it can't find ./src/main.js
  • vue-cli-service build ./client/src/main.js fails with a babel problem due to 'experimental' lazy loading syntax. Adjusting the babel.config.js file and re-running fails with the same error because the config file is in ./client. Moving babel.config.js up to the top level and trying again, it fails saying it can't find @/components/HelloWorld.vue. This is because the @ alias is broken when we run in the top level

Let's try to cd to the client first and repeat

  • cd ./client && vue-cli-service build initially appears to work, which is a bit weird because the *.config.js files are in ./client. However when I then serve this up with a web server, I get the completely unformatted Welcome page. Digging further it appears all the styles are missing and they don't appear to have been resolved during the build process.

If I run cd ./client && vue-cli-service serve I see the same behaviour as above - it appears to work but all Vuetify or Vue styles are missing

If I run vue inspect in my top level directory and in ./client, I can see where the problem stems from:

In the top level directory inspect output you have the modules and the whole resolveloader section pointing to the correct location for node_modules, but context, output, and resolve are all pointing to the wrong place.

{
  mode: 'development',
  context: 'C:\\myfullstackapp\\test',
  node: {
...
  },
  output: {
    path: 'C:\\myfullstackapp\\test\\dist',
    filename: 'js/[name].js',
    publicPath: '/',
    chunkFilename: 'js/[name].js'
  },
  resolve: {
    alias: {
      '@': 'C:\\myfullstackapp\\test\\src',
      vue$: 'vue/dist/vue.runtime.esm.js'
    },
...
    modules: [
      'node_modules',
      'C:\\myfullstackapp\\test\\node_modules',
      'C:\\myfullstackapp\\test\\node_modules\\@vue\\cli-service\\node_modules'
    ]
  },
  resolveLoader: {
    modules: [
      'C:\\myfullstackapp\\test\\node_modules\\@vue\\cli-plugin-babel\\node_modules',
      'node_modules',
      'C:\\myfullstackapp\\test\\node_modules',
      'C:\\myfullstackapp\\test\\node_modules\\@vue\\cli-service\\node_modules'
    ]
  },
...

In the ./client directory inspect output, it's the reverse and the context, output and resolve are all correct but all the node_module references are wrong:

{
  mode: 'development',
  context: 'C:\\myfullstackapp\\client',
  node: {
...
  },
  output: {
    path: 'C:\\myfullstackapp\\client\\dist',
    filename: 'js/[name].js',
    publicPath: '/',
    chunkFilename: 'js/[name].js'
  },
  resolve: {
    alias: {
      '@': 'C:\\myfullstackapp\\client\\src',
      vue$: 'vue/dist/vue.runtime.esm.js'
    },
...
    modules: [
      'node_modules',
      'C:\\myfullstackapp\\client\\node_modules',
      'C:\\myfullstackapp\\node_modules\\@vue\\cli-service\\node_modules'
    ]
  },
  resolveLoader: {
    modules: [
      'node_modules',
      'C:\\myfullstackapp\\client\\node_modules',
      'C:\\myfullstackapp\\node_modules\\@vue\\cli-service\\node_modules'
    ]
  },
...

So basically to support this full stack structure out of the box with vue-cli, we need to decouple the project location from the node_modules location. A simplistic approach would be to assume where you ran npm is where node_modules is located, and where you run vue-cli-service is the top level project directory, but you would still run into problems running vue add... or vue ui

I hope that explains it clearly!

@davydnorris
Copy link
Author

davydnorris commented Oct 23, 2019

So based on what I saw in the vue inspect output, I went back and hacked the vue.config.js file and I've managed to get a working system!

The easiest was to run everything from the full stack root, and adjust the three places where ./client should have been used. The package.json script commands were returned to their original form, (so no cd ./client in front), babel.config.js and vue.config.js were moved up to the top level directory, and I used the following in my config:

module.exports = {
    "outputDir" : 'client/dist',
    "transpileDependencies" : [ "vuetify" ],
    "configureWebpack" : {
        context: __dirname +'\\client\\',
        resolve : {
            alias : {
                '@' : __dirname +'\\client\\src'
            }
        }
    }
}

So i now have a work around once I have created the client Vue project and added all my plugins. What is also a surprise is that it appears vue ui also works if you import the project top level directory, but you only see the destination directory setting in the config page, not the webpack additions.

What I need to try is repeating the steps above to create the basic full stack project, configuring webpack, and then try vue add vuetify to see if that works. Would be very cool if it did

While this now seems to work, it would be nice to have this as a single setting somewhere. Can I ask you to consider it as a feature request?

@davydnorris
Copy link
Author

I've now been playing with this manual work around and it works really well, except for when you need to add plugins after you have restructured the project, for instance there's no way to get vue add vuetify to work unless you put everything back the way it was.

for those playing along at home, I added the following scripts in my package.json - the various options allow me to start the front or back end isolated, or the combined system in development and production mode:

"scripts": {
   "serve": "vue-cli-service serve",
   "build": "vue-cli-service build",
   "build:development": "vue-cli-service build --mode development",
   "lint": "vue-cli-service lint",
   "start": "npm run build && node ./server/server.js",
   "start:server": "node ./server/server.js",
   "dev": "npm run build:development && nodemon ./server/server.js",
   "dev:server": "nodemon ./server/server.js"
 },

@vue-bot
Copy link

vue-bot commented Nov 9, 2019

Hello!
This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues requiring feedback after 20 days of inactivity. It’s been at least 10 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. (A maintainer can also add the label not stale to keep this issue open.)

Thanks for being a part of the Vue community! 💪💚️

@Akryum
Copy link
Member

Akryum commented Nov 9, 2019

@davydnorris Here is an example of an app organized as a monorepo: https://github.com/Akryum/awesomejs.dev/

Using yarn workspaces allow you to have multiple "sub-projects" under the same "meta-project". It works very well to have proper package.json for the Vue CLI project, while not duplicating the node_modules and having a root package.json at the top.

@davydnorris
Copy link
Author

Hi @Akryum - thanks for that but as seen above I already have a very simple structure that works well... but it's a completely manual process every time, and once you make the changes you can no longer use vue-cli to add plugins if you need to.

What I'm proposing is to add a single property that would let you separate the vue directory location from the actual project location, while still playing nice with plugins

@davydnorris
Copy link
Author

Also @Akryum your structure uses two completely disconnected projects for front and back end - what I am proposing is to be able to also have a single project containing both front and back end, with a single run command that starts the back end and uses it to serve the front end.

My current structure allows me to use environment variables during deployment to select front, back or both on an instance by instance basis.

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

5 participants