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

Update node-sass troubleshooting information #2206

Merged
merged 1 commit into from
Sep 18, 2019

Conversation

csexton
Copy link
Contributor

@csexton csexton commented Aug 1, 2019

Probably obvious to people who are familiar with the tools, I wasn't sure how to fix the node-sass error as described. Hopefully these changes save some other folks some of the time it took me to dig into the sassy issue.

Updates the docs to with two bits of information:

  1. Add a cut-n-pasteable example for the changes needed in package.json
  2. Add the note about Heroku buildpacks

Was able to verify this works with Rails 6.0.0.rc2 🎉

@bbugh
Copy link
Contributor

bbugh commented Aug 6, 2019

I would not advise using the node buildpack first on Heroku because yarn will end up running multiple times. On our production setup with a large app, it doubled the build time.

You can track the issue here: heroku/heroku-buildpack-ruby#654

@csexton
Copy link
Contributor Author

csexton commented Aug 9, 2019

@bbugh I did not realize that. Bummer.

I noticed that @schneems recommends manually disabling the yarn:install task, but this would break local development. So I don't think that is a good option currently.

Frustrating thing is that the post install script was a hack to rebuild node-sass so Webpacker could find that binary. And the Node.js buildpack was a hack to get access to the npm binary.

Anyone have suggestions as for a better way to either:

  • Fix Webpackers path to node-sass
  • Get npm installed on Heroku

If it is the latter it may be best to open an issue on the ruby build pack repo.

What ever it is worth, at least this approach did work -- even if it re-installs node modules a few times in the process 😕

@bbugh
Copy link
Contributor

bbugh commented Aug 9, 2019

The easiest thing to do is to ditch node-sass.

  1. Add sass-loader > 7.1 to devDepencencies. This version will automatically use dart version of sass if it exists (see Make this package implementation-agnostic webpack-contrib/sass-loader#573)
  2. Remove node-sass
  3. Add sass package to devDependencies (which is the dart version that doesn't require any external dependencies)
  4. Add resolutions: { "sass-loader": "^7.2.0" } to your package.json file.

Voila, no more annoying node-sass constant failure and needing to rebuild.

Supposedly the dart version is a little slower than node-sass, but we have a giant project with a ton of CSS and the change was insignificant.

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Aug 9, 2019

@csexton Can you share what versions you are using? (node -v && npm -v && yarn -v) This is a yarn-specific problem that had since been fixed.

You also may be using a version of node that node-sass does not yet support on all platforms (node 12 was just added). I recommend installing node 10 via nVm.

As a last-ditch effort, you can try deleting your yarn.lock file and running yarn install --force.

@bbugh The method you describe will not override the sass-loader that webpacker comes with and it will cause problems in a production environment.

because yarn will end up running multiple times

Subsequent yarn installs should generally take under a second. Are you referring to the webpack build?

@bbugh
Copy link
Contributor

bbugh commented Aug 9, 2019

The method you describe will not override the sass-loader that webpacker comes with and it will cause problems in a production environment.

You're right, but only because I forgot to mention that you need to use yarn's resolutions in package.json to force the sass-loader version. It works fine for us in production. I corrected my post.

  "resolutions": {
    "sass-loader": "^7.2.0"
  },

Subsequent yarn installs should generally take under a second. Are you referring to the webpack build?

That's only true if yarn is defaulted to --prefer-offline which isn't the case in the webpacker version I have (4.0.2). Without that flag, yarn will always check the remote system, even if you have a lock file. The issue that I linked has more information about the problem.

@csexton
Copy link
Contributor Author

csexton commented Aug 9, 2019

@jakeNiemiec looks like my local machine is running Node 12, but that has been working. Heroku (with both Node and Ruby build packs) has 10.

Dev Machine Heroku
node 12.6.0 10.16.1
npm 6.10.1 6.9.0
yarn 1.17.3 1.17.3

Seems like installing nVm on the server wouldn't be the best approach, but I could setup a custom build pack pinned to a node version if the official one won't work (I'd just prefer not to if I can help it 😅).

@jakeNiemiec
Copy link
Member

nVm only required for local. I clarify because homebrew has problems. @csexton I recommend using node 10 until 12 is out of prerelease. There have been a lot of weird errors with 12

I forgot to mention...It works fine for us in production. I corrected my post.

You probably also addressed the fact that devDependencies are not installed in production environments. You aren't wrong to do this, but it gives an incomplete picture that has tripped up users in the past who don't understand the node.js flow as well as ruby.

Without that flag, yarn will always check the remote system

image

Even so, there may be a larger problem if the rake task takes more than a couple of seconds.

webpacker version I have (4.0.2)

Check out the releases since then, webpacker was compiling too much. It has since been fixed.

@csexton A problem I can see is going from a node12 env to node10, but I don't have any heroku experience. Can you tell me if downgrading local to node10, deleting your yarn.lock file, and running yarn install --force fixes anything? (it should regenerate the lock file with corrected deps)

@csexton
Copy link
Contributor Author

csexton commented Aug 15, 2019

Did some more testing between the different approaches.

Method 1: Dart-based Sass

I was able to get @bbugh's approach to work.

Removed node-sass and added Dart-based sass to package.json:

"devDependencies": {
-    "node-sass": "^4.12.0",
+    "sass": "^1.22.9",
     "sass-loader": "^7.1.0"
}
Full package.json
{
  "name": "My App",
  "private": true,
  "dependencies": {
    "@fortawesome/fontawesome-free": "5.5.0",
    "@rails/actioncable": "^6.0.0-rc2",
    "@rails/activestorage": "^6.0.0-rc2",
    "@rails/ujs": "^6.0.0-alpha",
    "@rails/webpacker": "^4.0.2",
    "add": "^2.0.6",
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1",
    "js-storage": "^1.0.4",
    "popper": "^1.0.1",
    "popper.js": "^1.15.0",
    "simple-line-icons": "2.4.1",
    "turbolinks": "^5.2.0",
    "yarn": "^1.16.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "sass": "^1.22.9",
    "sass-loader": "^7.1.0",
    "webpack-dev-server": "^3.3.1"
  }
}

Then was able to deploy to Heroku with only the Ruby build pack. 🎉

Method 2: Node 10

I tried to follow @jakeNiemiec's suggestion of downgrading Node on my development machine to v10, deleting the yarn.lock file, and pushing. But ran into errors.

Full package.json
{
  "name": "My App",
  "private": true,
  "dependencies": {
    "@fortawesome/fontawesome-free": "5.5.0",
    "@rails/actioncable": "^6.0.0-rc2",
    "@rails/activestorage": "^6.0.0-rc2",
    "@rails/ujs": "^6.0.0-alpha",
    "@rails/webpacker": "^4.0.2",
    "add": "^2.0.6",
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1",
    "js-storage": "^1.0.4",
    "popper": "^1.0.1",
    "popper.js": "^1.15.0",
    "simple-line-icons": "2.4.1",
    "turbolinks": "^5.2.0",
    "yarn": "^1.16.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "node-sass": "^4.12.0",
    "sass-loader": "^7.1.0",
    "webpack-dev-server": "^3.3.1"
  },
  "scripts": {
    "postinstall": "npm rebuild node-sass"
  }
}

But got a very sad Precompiling assets failed during the deploy:

Module build failed (from ./node_modules/sass-loader/lib/loader.js):
Error: ENOENT: no such file or directory, scandir 
Full Error Message
remote:            ERROR in ./app/javascript/theme/style.scss (./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/lib/loader.js??ref--7-3!./app/javascript/theme/style.scss)
remote:            Module build failed (from ./node_modules/sass-loader/lib/loader.js):
remote:            Error: ENOENT: no such file or directory, scandir '/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/vendor'
remote:                at Object.readdirSync (fs.js:785:3)
remote:                at Object.getInstalledBinaries (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/extensions.js:131:13)
remote:                at foundBinariesList (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/errors.js:20:15)
remote:                at foundBinaries (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/errors.js:15:5)
remote:                at Object.module.exports.missingBinary (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/errors.js:45:5)
remote:                at module.exports (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/binding.js:15:30)
remote:                at Object.<anonymous> (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass/lib/index.js:14:35)
remote:                at Module._compile (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/v8-compile-cache/v8-compile-cache.js:192:30)
remote:                at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
remote:                at Module.load (internal/modules/cjs/loader.js:600:32)
remote:                at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
remote:                at Function.Module._load (internal/modules/cjs/loader.js:531:3)
remote:                at Module.require (internal/modules/cjs/loader.js:637:17)
remote:                at require (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/v8-compile-cache/v8-compile-cache.js:161:20)
remote:                at getDefaultSassImpl (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/sass-loader/lib/loader.js:203:10)
remote:                at Object.sassLoader (/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/sass-loader/lib/loader.js:79:31)
remote:
remote:
remote:  !
remote:  !     Precompiling assets failed.
remote:  !

This was on Heroku with only the Ruby build pack.

I still don't fully grok the Webpack, Yarn, and NPM process; so could be doing something silly. If anything jumps out, or I can try something else, please let me know.

@jakeNiemiec
Copy link
Member

ENOENT: no such file or directory, scandir '/tmp/build_7e7d4fac8f5bb4a7dc2a1c8b13830865/node_modules/node-sass

As I stated above devDependencies are not installed in production environments. Move node-sass & sass-loader to dependancies.

Dart-based Sass only worked because node-sass was removed from the root package.json devDependencies (allowing it to be installed as a dependency of a dependency via ./node_modules/@rails/webpacker/package.json)

If you want to experiment with this yourself, yarn list --depth 1 will show you the resolved dependency tree. Additionally, you can imitate a production yarn install with yarn install --production.

@csexton
Copy link
Contributor Author

csexton commented Aug 16, 2019

Dart-based Sass only worked because node-sass was removed from the root package.json devDependencies (allowing it to be installed as a dependency of a dependency via ./node_modules/@rails/webpacker/package.json)

Thats interesting. So if a package is included in devDependencies it won't be installed in production even if it is a dependency of something included in dependencies? Sorry if I am slow to understand.

Based on your advice I tried a couple things:

1) Moving sass packages to production

I moved the two Sass packages to dependencies, and it worked:

   "dependencies": {
     "@rails/actioncable": "^6.0.0-rc2",
     "@rails/activestorage": "^6.0.0-rc2",
     "@rails/ujs": "^6.0.0-alpha",
     "@rails/webpacker": "^4.0.2",
     "add": "^2.0.6",
     "bootstrap": "^4.3.1",
     "jquery": "^3.4.1",
     "js-storage": "^1.0.4",
     "popper": "^1.0.1",
     "popper.js": "^1.15.0",
     "turbolinks": "^5.2.0",
     "yarn": "^1.16.0",
+    "node-sass": "^4.12.0",
+    "sass-loader": "^7.1.0"
   },
   "devDependencies": {
-    "node-sass": "^4.12.0",
-    "sass-loader": "^7.1.0",
     "webpack-dev-server": "^3.3.1"
   },

Yay, it worked!

🎉 Verifying deploy... done.

2) Removing sass packages entirely

And based on the comment about Dart Sass, I just tried deleting both references entirely:

   "dependencies": {
     "@rails/actioncable": "^6.0.0-rc2",
     "@rails/activestorage": "^6.0.0-rc2",
     "@rails/ujs": "^6.0.0-alpha",
     "@rails/webpacker": "^4.0.2",
     "add": "^2.0.6",
     "bootstrap": "^4.3.1",
     "jquery": "^3.4.1",
     "js-storage": "^1.0.4",
     "popper": "^1.0.1",
     "popper.js": "^1.15.0",
     "turbolinks": "^5.2.0",
     "yarn": "^1.16.0"
   },
   "devDependencies": {
-    "node-sass": "^4.12.0",
-    "sass-loader": "^7.1.0",
     "webpack-dev-server": "^3.3.1"
   },

Also worked!

🎉 Verifying deploy... done.


Now, the point of the PR in the first place. I'd like to update the docs with a good suggestion for how to get webpacker working on Heroku with a fresh Rails app. Would it be best to have them move the packages to the production dependencies or just delete them so that the tooling can sort out the dependency list?

@jakeNiemiec
Copy link
Member

So if a package is included in devDependencies it won't be installed in production even if it is a dependency of something included in dependencies?

It's ok. Most node.js folks have this burned into their brains from the 2016 npm@5 disaster. There are many obscure edge cases that don't translate well for rails users. See this for the webpacker history with this issue: #1880

If you have time, in #1212 (comment) & #1212 (comment) I discuss the technical reasons why you really shouldn't use devDeps at all with webpacker.

tl;dr: devDeps are for node.js libraries that other apps will install. It is solely a liability for webpacker. I tried to document that here:

Be careful not to add any build or app related JS modules in this fashion. Adding JS modules to devDependencies will block them from being installed in any production environment.

Docs from JS modules may instruct you to use --dev or devDependencies, but this is generally under the assumption that you are using a node.js workflow.


Would it be best to have them move the packages to the production dependencies

I'd say move everything to deps.


or just delete them so that the tooling can sort out the dependency list?

You should avoid doing this in order to future-proof against changes currently coming down the pipeline: #2112. Specificly:

packages cannot rely on cross package boundaries (e.g.: webpacker users can't rely on the sub-dependencies of @rails/webpacker as if they were direct dependencies)

Probably obvious to people who are familiar with the tools, I wasn't
sure how to fix the `node-sass` error as described. Hopefully these
changes save some other folks some of the time it took me to dig into
the sassy issue.

Updates the docs to with two bits of information:

1. Guidelines to move any webpack-related packages from
   `devDependencies` to `dependencies`
2. The instructions to rebuild `node-sass` if needed

Was able to verify this works with Rails 6.0.0
@csexton csexton force-pushed the docs-troubleshooting-sass branch from 0608407 to 51f478a Compare August 22, 2019 19:13
@csexton
Copy link
Contributor Author

csexton commented Aug 22, 2019

I edited the documentation with the lessons learned here. I have verified this is working with the Rails 6 release on Heroku.

With out the need to additional build packs 🎉

@gauravtiwari gauravtiwari merged commit 3f88de6 into rails:master Sep 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants