Browserify can't find local module - javascript

I have same issue but with local component.
That's my tree:
app
|
- assets
|
- js
|
- app.jsx
|
- MainComponent.jsx
But when I run command:
macpro:app user$ browserify assets/js/app.jsx > tesapp.js
It response error :
Error: Cannot find module 'MainComponent' from
'/Users/user/WebstormProjects/gowithme/app/assets/js'
That is code of app.jsx file :
var React = require('react');
var ReactDOM = require('react-dom');
var MainComponent = require('MainComponent');
console.log("Test",MainComponent);

It happens because you are using .jsx extension. By default browserify cannot recognize unknown extension. Add browserifyOptions to you grunt config, it might help:
options: {
transform: [['babelify', {presets: ['es2015', 'react']}]],
browserifyOptions: {
debug: true,
extensions: ['.jsx']
}
}
Also I suggest to omit usage of .jsx. No .jsx extension?

You need to give the relative path when you're loading a local module (i.e. a .js file of your own) with require:
var React = require('react');
var ReactDOM = require('react-dom');
var MainComponent = require('./MainComponent'); //added ./ so now the path is relative
console.log("Test",MainComponent);
When using an absolute path (like require('fs') or require('react')) node will try to load native modules (like fs, path etc) or modules inside node_modules.
https://nodejs.org/api/modules.html#modules_modules

Related

Storybook webpack absolute import

In our app we are using absolute paths for import modules. We have react folder into our resolve root:
Folder structure
We are using webpack for build and develop app and it works ok, with the next options:
resolve: {
modules: [
'node_modules',
path.resolve('src')
]
},
I'm working on integration of storybook and found, that it can't find any module from this react folder.
ERROR in ./stories/index.stories.js
Module not found: Error: Can't resolve 'react/components/Button' in 'project_name/stories'
# ./stories/index.stories.js
for the next line:
import Button from 'react/components/Button';
As mark: I added resolve/modules to .storybook/webpack config and also if I try to import anything other from, for example services/xxx - it works.
Issues
react folder name conflicts with actual React package location: node_modules/react. Webpack tries to resolve to .resolution(default is node_modules) if the file does not exist in the path.
.resolution is not appropriate for this sort of usage. it is mostly used for package resolution because it can't tell source strings.
to change path selectively, use alias instead.
Solution
change your component folder's name so that it does not collide with node_modules/react. a good example is view/components/Button.
add alias to .storybook/main.js setting
// .storybook/main.js
const path = require('path');
module.exports = {
/* ... other settings goes here ... */
/**
* #param {import('webpack').Configuration} config
* */
webpackFinal: async (config, { configType }) => {
if (!config.resolve) config.resolve = {};
// this config allows to resolve `view/...` as `src/view/...`
config.resolve.alias = {
...(config.resolve.alias || {}),
view: path.resolve(__dirname, '../src/view'),
};
return config;
},
};
change storybook code in accordance with (1)
// Button.stories.jsx
import Button from 'view/components/Button';
//...

How can I get PurifyCSSPlugin to remove my unused css in Angular6?

I'm trying to remove a lot of unused css within my sass files in my Angular 6 project.
I'm learned that there is a webpack plugin called PurifyCss.
Currently now, I'm unable to eject the webpack config in my angular project so I'm using ngw to help add the necessary plugins (to angular's webpack config) needed to extract the unused css in my sass files.
ngw.config.ts
import * as webpack from 'webpack';
import { Path } from '#angular-devkit/core';
import { NormalizedBrowserBuilderSchema } from '#angular-devkit/build-angular';
import * as PurifyCSSPlugin from 'purifycss-webpack';
import * as path from 'path';
import * as glob from 'glob';
export type WebpackOptions<T = NormalizedBrowserBuilderSchema> = {
root: Path,
projectRoot: Path,
options: T;
};
const command = process.argv[2].toLowerCase();
export default function (config: webpack.Configuration, options: WebpackOptions) {
if (command === 'test') {
console.log('Test configuration is running');
}
console.log('To modify webpack build, you can use ngw.config.ts');
console.log('check path:', glob.sync(path.join(__dirname, 'src/**/*.html')));
config.plugins.push(
new PurifyCSSPlugin({
// This was suggested to help it actually remove the css from: https://github.com/webpack-contrib/purifycss-webpack/issues/54
// Although there is an error of: Error: data['resolveExtensions'] should NOT have additional properties
resolveExtensions: ['.html', '.js'],
// This causes the build to run but does not remove the unused css
paths: glob.sync(path.join(__dirname, 'src/**/*.html'))
}),
);
return config;
}
Using the paths property alone doesn't work and it was suggested to add resolveExtensions from here.
Although this leads to the error below when doing a ngw prod build:
Error: data['resolveExtensions'] should NOT have additional properties
How can I configure the PurifyCSSPlugin to remove unused css within sass files in an Angular-6-cli environement?
Note: I don't have much experience with webpack, so I'm not sure if this config only works with css files instead of scss files. (If so please correct me).
Thanks

How to trick Node.js to load .js files as ES6 modules?

Node.JS 10 added experimental support for loading ES6 modules, which already work in browsers. That would mean that we could finally use exactly the same files for Node.JS and browsers without any transpiling or polyfills.
Except we can't. Node.js requires .mjs extension for files to be loaded as modules. I tried tricking node by using a symlink, but node got around it:
D:\web\lines>node --experimental-modules ES6test.mjs
(node:7464) ExperimentalWarning: The ESM module loader is experimental.
D:\web\lines\ES6test.js:6
import myLibrary from "./MyFile.mjs";
^^^^^^^^^^^^^^^
I can't think of any other workaround to make this work - which really renders the whole ES6 module support useless.
Can anybody else think of some trick to make Node.js ignore the extension?
You can now import .js file in node v12.x, in 2 steps:
Add the following line in your package.json file:
// package.json
{
"type": "module"
}
Add --experimental-modules flag before the script:
node --experimental-modules index.js
Reference: https://nodejs.org/api/esm.html
Node.js requires all ES modules should have .mjs extension. Since Node.js support of ES modules is experimental, this is subject to change. A proposal and open pull request are expected to address this problem with package.json esm flag and --mode option.
Currently this can be solved with custom ES module loader that hooks into default module resolver and changes module type for some modules:
custom-loader.mjs
import path from 'path';
const ESM_WITH_JS_EXT = './MyFile.js'; // relative to loader path
const ESM_WITH_JS_EXT_URL = new URL(path.dirname(import.meta.url) + `/${ESM_WITH_JS_EXT}`).href;
export function resolve(specifier, parentModuleURL, defaultResolver) {
const resolvedModule = defaultResolver(specifier, parentModuleURL);
if (resolvedModule.url === ESM_WITH_JS_EXT_URL)
resolvedModule.format = 'esm';
return resolvedModule;
}
It is used as:
node --experimental-modules --loader ./custom-loader.mjs ./index.mjs
Since there are fundamental differences in how ES and CommonJS modules are evaluated, the changes should be limited to modules that need them.
I solved exactly this problem with the fabulous esm package. You can enable dynamic (smart) esm module loading package wide, or per run with a flag like this:
node -r esm your/es6/module.js
It also has options to treat every file as a es6 module, or only those ending in '.mjs'. There are other packages out there, but this one just worked.
Import and export modules using ES6 that work with Node.js
Name files with .mjs extension instead of .js
Create files
touch main.mjs lib.mjs
main.js
import { add } from './lib.mjs';
console.log(add(40, 2));
lib.mjs
export let add = (x,y) => {
return x + y
}
Run
node --experimental-modules main.js
Here is a module that does what you need esmjs.mjs
import { readFileSync } from 'fs'
import { fileURLToPath, pathToFileURL } from 'url'
import { dirname, join } from 'path'
export const jsmodule = (test_url_or_path, module_path) => {
const __filename = test_url_or_path.toLowerCase().startsWith('file:')
? fileURLToPath(test_url_or_path)
: test_url_or_path
const __dirname = dirname(__filename)
const abs_path = join(__dirname, module_path)
const file_url = pathToFileURL(abs_path)
const file_buf = readFileSync(file_url)
const b64 = file_buf.toString('base64')
const moduleData = "data:text/javascript;base64," + b64
return import(moduleData)
}
Usage from .mjs module:
const { hey } = await jsmodule(import.meta.url, '../../test-data/mjs.js')
Usage, from .js file:
const { hey } = await jsmodule(__filename, '../../test-data/mjs.js')
Reference & tests on Github
You can do it in this way:
Create a file module2.mjs (the name is up to you)
'use strict';
export function foo() {
return 'foo';
}
Create index.mjs file:
'use strict';
import { foo } from './module2.mjs';
console.log(foo());
Using node 8.11.1 (or 10) you can run it as (command and output provided):
node --experimental-modules index.mjs
(node:60428) ExperimentalWarning: The ESM module loader is experimental.
foo

Webpack uglify lots of legacy code in a clean way

Currently I am implementing webpack in our websites. The websites are static websites build on a php Zend2 architecture. We have lots and losts of vendor module installed and updated by composer. I have a working webpack solution, hence I feel like the code for legacy minification could be better. I have the following constraints
Implement the old js code as is. I know rewriting to modules would be the best options but we have 100+ js files. The plan is to rewrite if the code needs to be touched.
Legacy code should be minified based on a env.NODE_ENV flag
current code splitting needs to be maintained
Currently the solution works as follow
Webpack switches to different js-entry files
module.exports = env => {
// webpack variables
const isProduction = env.NODE_ENV === 'production';
const assetJsPath = 'module/Eurocampings/assets/js/';
const assetJsLegacyPath = 'module/Eurocampings/assets/js-legacy/';
const webpackEntrySuffix = isProduction ? 'production' : 'develop';
const paths = {
entry: {
main: `${assetJsPath}main.js`,
mainLegacy: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/main-legacy.js`,
vendorBase: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/vendor-base.js`,
homepage: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/homepage.js`,
campsiteDetails: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/campsite-details.js`,
campsiteCompare: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/campsite-compare.js`,
campsiteChainDetails: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/campsite-chain-details.js`,
ajaxsolr: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/ajaxsolr.js`,
campsiteSearch: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/campsite-search.js`,
campsiteExtendedSearch: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/campsite-extended-search.js`,
picturefill: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/picturefill.js`,
destination: `${assetJsLegacyPath}webpack-entries-${webpackEntrySuffix}/destination.js`,
styles: './module/Eurocampings/assets/scss/style.scss',
},
output: {
folder: 'module/Eurocampings/dist',
}
};
Example of develop legacy entry without uglify-loader!
// webpack entry for develop
import exec from 'script-loader!../AcsiCampsiteSearch/DataLayerWidgetFactory.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/acsiCampsiteCompare.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Label.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Selector.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/CompareBox.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Cookie.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite-search/assets/js/search.js';
import exec from 'script-loader!../../../../../vendor/acsi/acsi-campsite-search/assets/js/map_initialization.js';
Example of production entry with uglify-loader!
// webpack entry for develop
import exec from 'script-loader!uglify-loader!../AcsiCampsiteSearch/DataLayerWidgetFactory.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/acsiCampsiteCompare.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Label.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Selector.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/CompareBox.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite/assets/js/acsiCampsiteCompare/extensions/Cookie.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite-search/assets/js/search.js';
import exec from 'script-loader!uglify-loader!../../../../../vendor/acsi/acsi-campsite-search/assets/js/map_initialization.js';

Webpack use node 'fs' module during buildtime so browser gets the result of function

Question: is there a way to tell webpack to tell built-in modules modules like fs to execute during build so the browser gets the result of this function, not the function call itself?
My Situation:
Currently I'm developing an application for the browser using webpack. I'm trying to use the node 'fs' module in one my files to require the index.js files from other directories. For example:
plugins
├──plugin1
│   ├── index.js (simply exports an object)
│
├──plugin2
│  ├── index.js (simply exports an object)
|
├──plugin3
│  ├── index.js (simply exports an object)
|
|──index.js (want to require all index.js from each plugin directory here)
I'm getting an error with webpack saying: Can't resolve 'fs' in somepath/node_modules/require-dir
My file index.js located at `plugins/index.js' which is simply trying to require my other files.
//module from NPM which uses the 'fs' module ('im not explicity using it)
const requireDir = require('require-dir');
const allPlugins = requireDir('./plugins/');
console.log(allPlugins);
Can't resolve 'fs' in '/some_path/node_modules/require-dir'
You have two options here.
I haven't used this personally, but you can use node config value as specified here.
node: {
fs: {true, "mock", "empty", false}
}
Set fs to any of the above values.
Don't use the fs module. It is a built/native modules which may or may not rely on native V8/C++ functions/libraries. Remember that webpack typically bundles assets for the browser. So instead of relying on a plugin, you can manually import your plugins like:
plugins/index.js
const plugin1 = require('./plugin1')
const plugin2 = require('./plugin2')
module.exports = {
plugin1,
plugin2
}
You could also use this answer to polyfill the require-dir module.
Thanks to Francisco Mateo's additional link about polyfilling require-dir, I learned about the context method that webpack adds to require.
This allows me to do dynamic requires like so in my plugins/index.js file:
//require all index.js files inside of /plugins directory
let context = require.context('.', true, /\index\.js/);
const loadPlugins = function(ctx){
let keys = context.keys();
let values = keys.map(context);
return values;
}
//array of values from each index.js require
console.log('loadPlugins', loadPlugins());

Categories