Using material-ui in client-side requireJS environment? - javascript

I've been researching for a couple hours now on how to include material-ui as a client-side AMD module for my application, and here is the closest I've come. Using grunt-amd-wrap, I used the following grunt task in my Gruntfile:
amdwrap: {
materialui: {
expand: true,
cwd: 'node_modules/material-ui/lib/',
src: ['**/*.js'],
dest: 'client/js/components/',
},
},
and scheduled it to run after material-ui finished transpiling to its lib folder. However, this still poses an issue since the files require() the following modules:
"inline-style-prefixer": "^0.5.1",
"lodash.throttle": "~3.0.4",
"lodash.debounce": "~3.1.1",
"react-addons-transition-group": "^0.14.0",
"react-addons-update": "^0.14.0",
"react-addons-create-fragment": "^0.14.0",
"react-addons-pure-render-mixin": "^0.14.0",
"warning": "^2.1.0"
And I can't figure out how to bundle those to a client-side AMD module in addition to material-ui itself.
In summary: Is there a way to use grunt to convert material-ui's CommonJS syntax to a self-contained AMD module (or set of self-contained AMD modules)? I'd like to avoid using gulp, and I'd prefer to only use browserify if it is absolutely necessary.

I ended up using grunt-webpack. Although it's not an AMD module, I was able to bundle the files I needed along with their dependencies in one output file.

Related

Integrating webpack into my WordPress workflow - Separate but related questions

This may be a bit of a long-winded one, but I am looking to use webpack going forward and can definitely see some advantages. However, some of the concepts are a little different to what I have been used to coming from a PHP-based environment and using Gulp.
There are a few things I am trying to get my head around so to help those interested with the post, I have highlighted my questions or what I am looking to achieve.
Background
I am building a boilerplate theme that has the usual WP files.
style.css
functions.php
sass
js
...
Amongst those files I have a directory called gutenberg. In this directory, I have a number of custom blocks (made using the #create-block package and ES5 syntax) that I am looking to package up similar to a React component. Each custom block folder contains:
{blockName}.php
index.js
style.scss
editor.scss
utils-{blockName}.module.js
What I am looking to do is generate a bundle.js file, but create separate files for each block so they can be imported on-demand in an attempt to reduce the initial bundle size. Ideally, this would be separate JS functions and CSS files.
What I have achieved so far
Working through the Webpack documentation I have managed to get a basic implementation going, but everything seems to compile into one and I am looking for a bit more control/chunking, and obviously my current output does not take advantage of Webpack. I can then import the required scripts (hopefully using an Intersection Observer) when a block starts to come into view. However, I'm not even sure this is possible. This is just my mind thinking what might be possible.
My deps are:
"dependencies": {
"#splidejs/splide": "^4.0.6"
},
"devDependencies": {
"#babel/core": "^7.18.2",
"#babel/preset-env": "^7.18.2",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"mini-css-extract-plugin": "^2.6.0",
"sass": "^1.52.2",
"sass-loader": "^13.0.0",
"style-loader": "^3.3.1",
"webpack": "^5.73.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.2"
},
My webpack config currently looks like this:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const entryPoints = {
app: "/js/app.js",
style: "/sass/main.scss",
editorStyle: "/sass/editor-styles.scss",
};
module.exports = {
entry: entryPoints,
devtool: "inline-source-map",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
clean: true,
},
mode: "development",
devtool: "inline-source-map",
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.s?[c]ss$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
};
and creates a dist similar or identical to this:
app.css
app.js
editorStyle.css
editorStyle.js
style.css
style.js
Now, the files that I am actually loading in are app.js, for my bundle. editorStyle.css for the styles in the editor, and style.css for the front-end styles. So am I right in saying I don't need app.css, editorStyle.js, and style.js? If so, how can I removed these from the build?
An attempt at code splitting
I did have a go at code splitting and got a result, but wasn't sure how I then use that chunk. I'm very new to ESM and have come from the standard one-long-js-file.js way of doing things
I have two blocks that use the same library, SplideJS. In those blocks utils-{blockName}.module.js I import Splide using the following method:
import Splide from '#splidejs/splide';
import '#splidejs/splide/css/core';
...exported functions...
In my webpack config I included the following:
optimization: {
splitChunks: {
chunks: 'all',
},
},
Which created a chunk file along the lines of vendor_splide-....-...-...{hash}.js. When I have a chunk like this, can I conditionally import it? The naming convention is obviously a standard method, can I have any control over how these chunks are named?
Bundling
One thing I did come across was the size of the bundle once Splide was imported. Jumping from 8kb to around 70kb in development, and over 80kb in production. Splide claims their package is around the 30-40kb size so I wondered was I importing it multiple times. I read that including source maps is expensive and removed devtool: "inline-source-map" from my config but it actually increased the bundle size, only slightly. Is it common for there to be discrepancies in claimed dist sizes of packages, or am I importing the library incorrectly (based on my above import statements)?
Any help with integrating or answering some of my early questions would be greatly appreciated.

How to build Typescript library with high compatibility for users?

What I want to do
I want to create a library that can be used as JS developer and as Typescript developer (frontend, no nodejs). That means that JS developer can use the library as inline script: <script type="text/javascript" src="mylib.js">var myLib = new MyLib();</script> and Typescript developers can use the library via imports.
What I need
I want to develop this library in Typescript and that's why I need a (build) solution that meets the following requirements:
The library should be developed in Typescript.
The build should contain all declarations automatically. Typescript developers should be able to import parts of this library.
For Javascript users the library should be used "the old-fashioned-way" as inline code in a <script>-Tag
The builds should be generated automatically if npm run build is called.
Generally the compatibility to older browsers of this library should be as high as possible.
What I tried so far
I created a typescript library and experimented with some settings in the tsconfig.json. The only way I found is to set the "module" attribute to "none", but then I can't use imports, exports in my code and have to merge all my Typescript files into one file. That would make the developement more difficult.
I looked on the repository e.g. of Konva.js, which does what I want to do. But I don't understand how it works. It seems the developer converts the code to umd and then uses rollup.js and at the end of the build there exists a javascript file as long as all Typescript code that is needed.
Comment
I'm looking for a solution for some days, but it's hard to find the perfect term that represents what I'm looking for. That's why I ask it here and hope someone can help me.
Browser does not understand typescript directly so that why using the typescript compiler we convert our typescript code to javascript. So when we build a typescript project or module we usually have a typescript configuration file tsconfig.json which a typescript compiler uses when compiling typescript to javascript.
This is only a snapshot of a few configurations in the tsconfig.json file. First, so you want to target most of the browser so what you will do set the target property to es5. Because most browsers older and new have very good support for it. Now your typescript compiler will emit javascript in the es5 version. There is the other property call outDir which will contain your outputted javascript code which you can import directly in your other javascript file in production or others can import it using tags.
In your package.json file, you can set the build script so it should run a typescript compiler to compile typescript code.
About point number 2, what you can do is you can specify the entry point of your typescript module like it can be main.ts or index.ts in package.json so when other typescript developer uses this library then there build tool would automatically get the import defined in the entry point of your library. So What would go in this entry point file is a question? It would contain export of your all the module of the library. In this way, all modules of your library would be available to other typescript users.
You can also check the konvasjs tsconfig file. what they are doing they are outputting their javascript files in the lib directory which is used in production. for development, they are working with normal typescript files.
I went deeper into package builder and found out: iife is the term I was looking for. A iife build allows to load a library directly via the script tag without anything else.
After hours of testing I found a perfect solution to build my library that fits my needs: rollup.js
The core npm packages are:
rollup: package builder.
#rollup/plugin-typescript: TS plugin in order to compile TS.
#rollup-plugin-generate-package-json: Automatically copy package.json, change it and move it to the output directory.
My solution creates a package with this structure:
|dist
|- js
|-- MyLibrary.js
|- es
|-- // ES files
|- package.json
The MyLibrary.js file can be imported easily within a <script> tag and the Typescript related files are in the "es" folder. The package automatically directs to the es/index.js that means Typescript developers should be able to use auto-complete for type suggestions in their IDE.
You can find a sample repository here:
https://github.com/julianpoemp/rolltsup-sample
package.json:
{
"name": "my-lib",
"version": "0.0.1",
"description": "",
"main": "src/main.js",
"dependencies": {
"rollup": "^2.38.5"
},
"devDependencies": {
"#rollup/plugin-commonjs": "^17.1.0",
"#rollup/plugin-typescript": "^8.1.1",
"rollup-plugin-generate-package-json": "^3.2.0",
"tslib": "^2.1.0",
"typescript": "^4.1.3"
},
"scripts": {
"build": "rollup --config rollup.config.js && rollup --config rollup.config_es.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
}
rollup.config.js
import typescript from '#rollup/plugin-typescript';
// rollup.config.js
export default {
input: 'src/index.ts',
output: {
file: 'dist/js/myLibrary.js',
name: 'MyLibrary',
format: 'iife'
},
plugins: [
typescript({
target: "ES5",
declaration: true,
outDir: "dist/js",
rootDir: "src"
})
]
};
rollup.config_es.js
import typescript from '#rollup/plugin-typescript';
import generatePackageJson from 'rollup-plugin-generate-package-json'
// rollup.config.js
export default {
input: 'src/index.ts',
output: {
dir: 'dist/es',
name: 'MyLibrary',
format: 'es'
},
plugins: [
typescript({
target: "ES5",
declaration: true,
outDir: "dist/es",
rootDir: "src"
}),
generatePackageJson({
outputFolder: "dist",
baseContents: (pkg) => {
pkg.main = "es/index.js";
pkg.scripts = undefined;
return pkg;
}
})
]
};

Using local modules with Webpack 4 is bundling the same dependency multiple times

I am attempting to find a good way to use local modules in npm, or a way of structuring a large application so it can be bundled off into modules which may or may not be in a separate repository.
Each local module has it's own package.json and dependencies which are installed.
My requirements are that the modules are written in ES6 and only compiled as part of the main project being built (so I don't have lots of dependencies being indiependently built constantly).
Project structure
/root
/main-module
... main js files <- entry point
webpack.config.js
package.json
/module-1
... module 1 js files
package.json
/module-2
... module 2 js files
package.json
/module-3
... module 3 js files
package.json
I'm currently investigating using local modules via specifying a local file in my package.json like so:
...
"dependencies": {
"lodash": "^4.17.10",
"module-1": "../module-1",
"module-2": "../module-2",
"module-3": "../module-3",
"normalize.css": "^8.0.0"
}
...
You can see the whole project here: https://github.com/SamStonehouse/webpack-local-modules-test
I'm using webpack with the babel-loader which doesn't need any extra setup in order to use this form and even watches the module file for changes and rebuilds when they're complete which is amazing.
Issue: once this has built lodash is included in the built bundle 4 times over, one for each module which requires it, even though they all require the same version and all the sources are compiled at the same time.
I've tried using the splitChunkPlugin but to no avail
I've tried setting lodash as a devDependency in the local modules (this was something I didn't want to do but it didn't work anyway)
Does anyone have a solution for this?
Or an alternative way of bundling local modules in a similar fashion
Change each of the modules to have lodash as a peer dependency instead of a direct dependency. So in the package.json file, change this:
"dependencies": {
"lodash": "^4.17.5"
}
To:
"peerDependencies": {
"lodash": "^4.17.5"
}

Webpack error from UglifyJS when creating production assets

I am using Webpack Encore (similar to Webpack) to create asset files.
Everything works well when creating the development files, however when running to command to generate the minified files for production I get an error.
ERROR Failed to compile with 1 errors
app.js from UglifyJS .
Invalid assignment [app.js:131,42]
Unexpected character '`' [app.js:10953,9]
It seems like the issue is caused by two plugins:
jquery-asScrollable and jquery-asScrollbar
In fact commenting out the following lines, the process completes successfully
require('jquery-asScrollable');
require('jquery-asScrollbar');
My package.json file
{
"devDependencies": {
"#symfony/webpack-encore": "^0.17.1",
"animsition": "^4.0.2",
"bootstrap": "4.0.0-beta.2",
"jquery": "^3.2.1",
"jquery-asScrollable": "^0.4.10",
"jquery-asScrollbar": "^0.5.7",
"jquery-mousewheel": "^3.1.13",
"node-sass": "^4.7.2",
"popper.js": "^1.12.9",
"sass-loader": "^6.0.6",
"webpack-notifier": "^1.5.0"
}
}
UPDATE:
app.js
require('jquery-asScrollable');
require('jquery-asScrollbar');
require('jquery-mousewheel');
Any ideas of what the problem might be?
The two plugins make use of ES6 syntax - in this case template literals. Uglify is incompatible with large parts of ES6. You need to either copy those plugins to your project code (so they get transpiled by your Babel) or un-exclude their respective node_modules folders in your Babel transpilation process.
See the template literals (and more ES6) being used here (line 90-93):
Another option is to import/require the minified versions of the plugins from their dist folders.
Example:
require('node_modules/jquery-asScrollable/dist/jquery-asScrollable.min.js')

Should vendor assets be included in version control with bower + rails?

I've started to use bower-rails to manage css/js assets in my rails projects.
By default the dependences are being installed in vendor/assets/bower_components.
The problem is that bower copies the whole packages, including sources, examples, licenses, etc.
I don't have problem to get rid of all those files, but I'm wondering if is necessary to include even the important files, as I think it should be the programmer's responsibility to load those dependences in the computer where is loading the project(maybe with grunt?), besides is supposed you should not touch the vendor packages as they are not your responsibility(regarding all those crappy files I want to delete).
My point is: Is there any kind of "best practice" related with bower packages and version control?
I recently used bower-rails for the first time and had Git ignore the vendor/assets/bower_components directory to good effect.
If you choose to have Git ignore bower_assets, you SHOULD specify a known good version of each library in bower.json instead of using latest to avoid version conflicts.
I'm using bower and bower-installer in my Rails 4.2.x app, without using any gems for javascript dependencies. I'm still using the asset pipeline.
I added vendor/assets/bower_components to my .gitignore file. I use bower-installer to copy just what I need to vendor/assets/{javascripts,stylesheets}, which are in source control.
I think that this gives me the best of both worlds: version control of JS libraries so updates are relatively easy, but no chance of a production deploy failing because someone removed 'leftpad' from the node repo.
Here's a trimmed-down version of my bower.json file (in source control). Note that jquery-form is not in the bower repo, so I included the path to the download file.
{
"name": "icots",
"main": "",
"private": true,
"ignore": [
"**/.*",
"bower_components",
"vendor/assets/bower_components",
"lib"
],
"dependencies": {
"jquery": "^3.1.1",
"jquery-ui": "^1.12.1",
"jquery.form": "http://malsup.github.io/min/jquery.form.min.js"
},
"install": {
"path": {
"js": "vendor/assets/javascripts",
"css": "vendor/assets/stylesheets",
"/[sc|le]ss$/": "vendor/assets/stylesheets"
},
"sources": {
"jquery": "vendor/assets/bower_components/jquery/dist/jquery.min.js",
"jquery-ui": "vendor/assets/bower_components/jquery-ui/jquery-ui.min.js",
"jquery-form": "vendor/assets/bower_components/jquery.form/index.js"
}
}
}

Categories

Resources