Babel aliases. WebStorm doesn't recognize sub-directories - intellij-idea

I recently moved from webpack path aliases to babel-plugin-module-resolved as it integrates better with testing frameworks.
.babelrc
{
"presets": [
["env", { "modules": false } ],
"react",
"stage-2",
"flow"
],
"plugins": [
["module-resolver", {
"alias": {
"static": "./static",
"common": "./src/common",
"data": "./src/data"
}
}],
["styled-jsx/babel", { "plugins": ["styled-jsx-plugin-sass"] }],
"react-hot-loader/babel"
]
}
WebStorm automatically recognizes imports for static/.. but can't resolve imports like common/.. and data/...
Is it possible to somehow instruct IDE about this configuration?
P.S. Right now I have src directory marked as Resource Root but this doesn't quite work as well.

One option I've taken is to create a fake webpack file that creates the same aliases. Leaves an unnecessary file in your codebase, but fixes all the name resolution issues.
Example:
webpack.junk.js
module.exports = {
resolve: {
alias: {
'static': path.resolve(__dirname, './static'),
'#common': path.resolve(__dirname, './src/common'),
'#data': path.resolve(__dirname, './src/data'),
},
},
};

Following SimplyComplexable's solution (I can't comment on his reply, sorry), I've create a new webpack.intellij.js that loads the project's webpack config (webpack.js), reads the .babelrc file to generates the resolve.alias section for the exported config, then explicitly pointed IntelliJ to this file in the preferences:
const path = require('path');
const fs = require('fs');
const { merge } = require('webpack-merge');
const webpackConfig = require('./webpack.js');
const babelRc = JSON.parse(fs.readFileSync('./.babelrc').toString('utf8'));
const aliases = {};
for (let i = 0; i < babelRc.plugins.length; i+=1) {
if (Array.isArray(babelRc.plugins[i]) &&
babelRc.plugins[i][0] === 'module-resolver') {
const a = babelRc.plugins[i][1].alias;
for (const entry of Object.entries(a)) {
aliases[entry[0]] = path.resolve(__dirname, entry[1]);
}
}
}
module.exports = merge(webpackConfig, {
resolve: {
alias: aliases
}
});
This solution allows me to have the aliases configured in one single place (.babelrc) while still having IntelliJ recognise them :)

Related

Avoid browser caching after deploy a Vuejs app

My Vuejs App did not update after deployment for production, every time require "Empty cache and hard reload" to get the updates, I tried a lot of solutions to apply versioning to generated files after build but none of them worked for me, I need a solution to apply new hash for all files after every single build, not just the updated ones.
My vue.config.js file content:
const path = require("path");
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
runtimeCompiler: true,
configureWebpack: {
resolve: {
alias: {
// If using the runtime only build
// vue$: "vue/dist/vue.runtime.esm.js" // 'vue/dist/vue.runtime.common.js' for webpack 1
// Or if using full build of Vue (runtime + compiler)
vue$: 'vue/dist/vue.esm.js', // 'vue/dist/vue.common.js' for webpack 1
'#': path.resolve('src'),
src: path.resolve('src'),
assets: path.resolve('src/assets'),
components: path.resolve('src/components'),
services: path.resolve('src/services'),
}
},
output: {
filename: '[name].[hash].js',
},
},
chainWebpack: config => {
config.module
.rule("eslint")
.use("eslint-loader")
.tap(options => {
options.configFile = path.resolve(__dirname, ".eslintrc.js");
return options;
});
},
};
Thanks in advance.
Welcome to the Vue JS cache nightmare. Did you try changing the version value in your package.json? I use to increment the value on each release as per x.y.z semantinc versioning. Maybe doing something like this:
{
"name": "My app",
"version": "1.0.15",
"private": true,
...
}

ESM library generated with rollup-plugin-postcss throws Cannot find module '../node_modules/style-inject/dist/style-inject.es.js'

We are maintaining an internal library which is exporting ESM modules using Rollup. We have just recently switched to using CSS modules, which we have set with rollup-plugin-postcss. We want to inject these styles into the head rather than have an external file.
Our built bundle generates the ESM file with:
import styleInject from '../node_modules/style-inject/dist/style-inject.es.js';
Our consuming library then fails with
Uncaught Error: Cannot find module '../node_modules/style-inject/dist/style-inject.es.js'
I would expect the ESM export to import styleInject from 'style-inject' and style-inject to be included in the package-lock.json as a dependency. What is the correct way of using CSS Modules and injecting into the head for the consumer of a library?
rollup.config.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import babel from '#rollup/plugin-babel';
import json from '#rollup/plugin-json';
import postcss from 'rollup-plugin-postcss';
import pkg from './package.json';
import fg from 'fast-glob';
import path from 'path';
export default [
{
input: 'src/index.js',
external: external(),
output: [
{
name: '#my/packageName',
file: pkg.module,
format: 'es',
sourcemap: true,
},
],
plugins: [
{
name: 'watch-external',
async buildStart() {
const files = await fg(['src/index.d.ts', 'playground/**/*']);
for (let file of files) {
this.addWatchFile(path.resolve(file));
}
},
},
json(),
postcss({
modules: true,
}),
babel({
exclude: /node_modules/,
babelHelpers: 'runtime',
babelrc: false,
presets: [
[
'#babel/preset-env',
{
modules: false,
useBuiltIns: 'entry',
corejs: 3,
targets: {
ie: 11,
},
},
],
'#babel/preset-react',
],
plugins: [
'#babel/plugin-transform-runtime',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-export-namespace-from',
],
}),
commonjs(),
],
},
];
function external() {
const { dependencies = {}, peerDependencies = {} } = pkg;
const externals = [
...Object.keys(dependencies),
...Object.keys(peerDependencies),
];
return id =>
// match 'lodash' and 'lodash/fp/isEqual' for example
externals.some(dep => id === dep || id.startsWith(`${dep}/`));
}
There is a bug in the library where the import is relative rather than being the module name.
There is an open pr, but the library looks like it is no longer being maintained. There are two recommended solutions in the comments:
String replace the built output
Add a custom rollup plugin to do this
This is not a bug at all. It works properly because stylesInject is just a function from 'styles-inject' package, so we can allow it to be bundled. But it is not bundled because most likely you have this setting in your rollup.config.js:
external: [
/node_modules/
]
So, replace it with a code bellow and it will work:
external: (id) => {
if (/style-inject/.test(id)) return false;
if (/node_modules/.test(id)) return true;
return false;
},
also, you can write a regexp for it instead
You need to do 2 things:
Add style-inject dependency in package.json
"dependencies": {
"style-inject": "^0.3.0"
},
Add following plugin to rollup.config.js
const plugins = [
...
{
/**
* - https://github.com/egoist/rollup-plugin-postcss/issues/381#issuecomment-880771065
* - https://lightrun.com/answers/egoist-rollup-plugin-postcss-esm-library-generated-with-rollup-plugin-postcss-throws-cannot-find-module-node_modulesstyle-in
*/
name: 'Custom Rollup Plugin`',
generateBundle: (options, bundle) => {
Object.entries(bundle).forEach((entry) => {
// early return if the file we're currently looking at doesn't need to be acted upon by this plugin
if (!entry[0].match(/.*(.scss.js)$/)) {
return;
}
// this line only runs for .scss.js files, which were generated by the postcss plugin.
// depending on the use-case, the relative path to style-inject might need to change
bundle[entry[0]].code = entry[1].code.replace(
/\.\.?\/[^\n"?:*<>|]+\/style-inject\/dist\/style-inject.es.js/g,
'style-inject',
);
});
},
}
];
References:
https://github.com/egoist/rollup-plugin-postcss/issues/381#issuecomment-880771065
https://lightrun.com/answers/egoist-rollup-plugin-postcss-esm-library-generated-with-rollup-plugin-postcss-throws-cannot-find-module-node_modulesstyle-in

Webpack Vue component with css <style> tags fails to build with Module parse failed: Unexpected token

Starting with a clean vue project, I was having issues building .vue components from PrimeVue.
These are ready made components and should really not fail to build.
Everytime I try to build, it fails to do so, and seems to fail with the line pointer at the start of the CSS styles.
ERROR in ./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css& (./node_modules/vue-loader/lib??vue-loader-options!./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css&) 340:0
Module parse failed: Unexpected token (340:0)
File was processed with these loaders:
* ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
|
> .p-slider {
| position: relative;
| }
# ./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css& 1:0-119 1:135-138 1:140-256 1:140-256
# ./node_modules/primevue/components/slider/Slider.vue
# ./node_modules/primevue/slider.js
# ./myproject/components/Test.js
# ./myproject/components/App.js
# ./myproject/main.js
This is my webpack config file:
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
mode: 'development',
entry: 'main.js',
output: {
filename: 'main.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
};
What is causing this error, as I am importing the components correctly as stated by the PrimeVue documentation.
Setting a rule in the webpack config to send the .vue files for processing to vue-loader is not enough.
You need to specify how to handle .css files too, and this then gets applied to tags in a .vue file as well. Without this rule, it will not know what to do with the <style> blocks, even if you dont plan on using the .css file part of this.
Update the rules section of the webpack.config.js with the following:
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
},
// this will apply to both plain `.css` files
// AND `<style>` blocks in `.vue` files
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
Also make sure that vue-style-loader and css-loader are installed in package.json.
More information can be found at the manual installation section of the vue-loader documentation, specifically the code example under 'A more complete example webpack config will look like this.'
I would highly recommend to cache the .vue files because they will slow down your build time in big projects.
// snippet from https://github.com/unic/darvin-webpack-boilerplate/blob/master/webpack/settings/javascript-vue/index.js
const {VueLoaderPlugin} = require('vue-loader');
const webpack = require('webpack');
const path = require('path');
const ROOT_PATH = process.cwd();
const CACHE_PATH = path.join(ROOT_PATH, 'tmp/cache');
const VUE_VERSION = require('vue/package.json').version;
const VUE_LOADER_VERSION = require('vue-loader/package.json').version;
const dev = {
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
cacheDirectory: path.join(CACHE_PATH, 'vue-loader'),
cacheIdentifier: [
process.env.NODE_ENV || 'development',
webpack.version,
VUE_VERSION,
VUE_LOADER_VERSION,
].join('|'),
},
}
]
},
plugins: [
new VueLoaderPlugin(),
],
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json'],
},
};

Storybook with craco - call a different verson of react-scripts

Storybook currently calls react-scripts. However, I've got some parts of the CRA config overriden with craco. It means my application is invoked with craco ..., rather than react-scripts ....
Is there a clean solution to have Storybook call craco instead?
The solution I came up with is this :
.storybook/main.js :
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.js'],
addons: [
'#storybook/preset-create-react-app',
'#storybook/addon-actions',
'#storybook/addon-links',
'#storybook/addon-viewport/register',
'#storybook/addon-knobs/register',
],
webpackFinal(config, { configType }) {
return {
...config,
resolve: {
alias: {
...config.resolve.alias,
'~': path.resolve(__dirname, '../src/'),
},
},
};
},
};
I was only using the alias feature in my craco file, so here I override webpack config from storybook and only add the alias parameter. For your case, you'll need to add your own config.
The #FR073N solution is good, but since the lasts versions, this throw an error.
One line was missing to fully override correctly the webpack config.
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.js'],
addons: [
'#storybook/preset-create-react-app',
'#storybook/addon-actions',
'#storybook/addon-links',
'#storybook/addon-viewport/register',
'#storybook/addon-knobs/register',
],
webpackFinal(config, { configType }) {
return {
...config,
resolve: {
...config.resolve, // <= HERE
alias: {
...config.resolve.alias,
'~': path.resolve(__dirname, '../src/'),
},
},
};
},
};
I've successfully used storybook-preset-craco with #storybook#6.3.5 and react-scripts#4.0.3 and #craco/craco#6.2.0 in a new CRA TypeScript project.

Why are my js files being loaded as `index.html` instead of the actual js files created by webpack?

I'm creating a simple build from webpack, using typescript, jade, and stylus. When the final index.html file is spit out, however, it seems to think the js files are just the index.html file and not the actual js files bundled up by webpack and dynamically inserted at the bottom of the html body.
My project directory structure looks like this:
- dist (compiled/transpiled files)
- server
- dependencies
- index.js
- app.js
- app.[hash].js
- polyfills.[hash].js
- node_modules
- src
- server
- dependencies
- index.ts
- app.ts
- client (ng2 ts files)
- index.jade
This is my webpack build:
'use strict';
const webpack = require('webpack');
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const WebpackShellPlugin = require('webpack-shell-plugin');
const rootDir = __dirname;
/**
* Resolve paths so that we don't have to use relative paths when importing dependencies.
* Very helpful when scaling an application and changing the location of a file that my require another file
* in the same directory as the one it used to be in
*/
const pathResolves = [path.resolve(rootDir, 'src'), path.resolve(rootDir, 'node_modules')];
console.log('path', path.resolve(rootDir, 'src/server'));
module.exports = {
entry: {
'app': path.resolve(rootDir, 'src/client/main.ts'),
'polyfills': [
'core-js/es6',
'core-js/es7/reflect',
'zone.js/dist/zone'
]
},
output: {
path: path.resolve(rootDir, 'dist'),
filename: '[name].[hash].js'
},
module: {
rules: [
{
test: /\.component.ts$/,
use: [
{
loader: 'angular2-template-loader'
},
{
loader: 'ts-loader',
options: {
configFileName: path.resolve(rootDir, 'tsconfig.client.json')
}
}],
include: [path.resolve(rootDir, 'src/client')]
},
{
test: /\.ts$/,
use: [
{
loader: 'ts-loader',
options: {
configFileName: path.resolve(rootDir, 'tsconfig.client.json')
}
}
],
exclude: /\.component.ts$/
},
{
test: /\.jade$/,
use: ['pug-ng-html-loader']
},
{
test: /\.styl$/,
use: [
{ loader: 'raw-loader' },
{ loader: 'stylus-loader' }
]
}
]
},
resolve: {
extensions: ['.js', '.ts', '.jade', '.styl'],
modules: pathResolves
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'polyfills'
}),
new HTMLWebpackPlugin({
template: path.resolve(rootDir, 'dist/index.html')
}),
/**
* Define any environment variables for client
*/
new webpack.DefinePlugin({
APP_ENV: JSON.stringify(process.env.APP_ENVIRONMENT || 'development')
}),
/**
* This plugin is required because webpack 2.0 has some issues compiling angular 2.
* The angular CLI team implemented this quick regexp fix to get around compilation errors
*/
new webpack.ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
'./'
)
]
};
And finally, this is the src/server/app.ts file that serves up index.html:
import * as express from 'express';
import * as fs from 'fs';
import * as morgan from 'morgan';
import {
Config
}
from './dependencies/config';
export
function app(Container) {
const app = express();
const config: Config = Container.get(Config);
if (config.log.dev) {
app.use(morgan('combined'));
}
app.get('/', (req: express.Request, res: express.Response) => {
const indexPath: string = `dist/index.html`;
const encodeType: string = `utf-8`;
const html = fs.readFile(indexPath, encodeType, (err: Error, result: string) => {
if (err) {
return res.status(500).json(err);
}
return res.send(result);
});
});
return app;
}
The browser console shows the following 404 error messages (they're red in the browser console) when i go to localhost:3000:
GET http://localhost:3000/polyfills.9dcbd04127bb957ccf5e.js
GET http://localhost:3000/app.9dcbd04127bb957ccf5e.js
I know it's supposed to be getting the js files from dist/[file].[hash].js, but can't seem to make it work with webpack. Also, I should note that I set NODE_PATH to ./ in my gulp nodemon config. Any ideas why this isn't working?
Figured it out on my own. Forgot to add app.use(express.static('dist')) middleware to the app.ts file.