How can I remove bower from this project and use requirejs with yarn (noob)? - javascript

How can I switch this to avoid using bower?
I installed yeoman for the first time and the generator for knockoutjs use bower. Now I read bower support is limited and bootstrap use popper.js which in v2 will deprecate support for bower. I would like to avoid the headache now and learn at the same time.
RequireJS and every client side libraries is in /src/bower_modules.
If I install bootstrap using npm or yarn it will install them in /node_modules, which the browser doesn't have access.
Do I then use gulp to transfer the dist folder to my /src/bower_modules folder?
Folder structure:
/src/
|--bower_modules/
|--app/
|--require.config.js
/node_modules/
/gulpfile.js
gulpfile.js:
var requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('src/app/require.config.js') + '; require;'),
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
out: 'scripts.js',
baseUrl: './src',
name: 'app/startup',
paths: {
requireLib: 'bower_modules/requirejs/require'
},
include: [
'requireLib',
'components/nav-bar/nav-bar',
'components/home-page/home',
'text!components/about-page/about.html'
],
insertRequire: ['app/startup'],
bundles: {
// If you want parts of the site to load on demand, remove them from the 'include' list
// above, and group them into bundles here.
// 'bundle-name': [ 'some/module', 'another/module' ],
// 'another-bundle-name': [ 'yet-another-module' ]
}
}),
transpilationConfig = {
root: 'src',
skip: ['bower_modules/**', 'app/require.config.js'],
babelConfig: {
modules: 'amd',
sourceMaps: 'inline'
}
},
babelIgnoreRegexes = transpilationConfig.skip.map(function(item) {
return babelCore.util.regexify(item);
});
app/require.config.js:
var require = {
baseUrl: ".",
paths: {
"bootstrap": "bower_modules/bootstrap/js/bootstrap.min",
"crossroads": "bower_modules/crossroads/dist/crossroads.min",
"hasher": "bower_modules/hasher/dist/js/hasher.min",
"popper": "bower_modules/popper.js/dist/popper",
"jquery": "bower_modules/jquery/dist/jquery",
"knockout": "bower_modules/knockout/dist/knockout",
"knockout-projections": "bower_modules/knockout-projections/dist/knockout-projections",
"signals": "bower_modules/js-signals/dist/signals.min",
"text": "bower_modules/requirejs-text/text"
},
shim: {
"bootstrap": { deps: ["popper", "jquery"] }
}
};
Sidenote: The origin of the issue is that I require popper for bootstrap and bootstrasp.bundle is not included in the bower version is seems. Also popper doesn't like bower very much and won't be supported very long. I also have multiple errors trying to include it. I would also like to learn the good way and since bower will not be around long I wouldn't mind not working with it at all.

Bower itself posted a blog about this recently: https://bower.io/blog/2017/how-to-migrate-away-from-bower/.
Here's the key takeaways:
Manually move any packages from bower.json to package.json that are available from both Bower and NPM
For any items that are only available via Bower you can use the bower-away NPM package to install those with NPM instead of Bower
Write a task runner script (Grunt/Gulp/etc...) to move the package(s) file(s) to your dist directory
Something like this should do it.
gulp.task('injectNpmPackages', function () {
return gulp.src([
path.join('/node_modules/my-package/build/my-package.min.js')
])
.pipe(gulp.dest('/dist/vendor/'));
});

Related

How To Setup Custom ESBuild with SCSS, PurgeCSS & LiveServer?

Background:
I have a Webpack setup that I use to preprocess SCSS with PurgeCSS with a live HMR server with esbuild-loader for speeding up compiles in Webpack but even then my compile times are still slow and I would like the raw-speed of ESBuild and remove Webpack setup altogether.
The basic setup of ESBuild is easy, you install esbuild using npm and add the following code in your package.json:
{
...
"scripts": {
...
"watch": "esbuild --bundle src/script.js --outfile=dist/script.js --watch"
},
...
}
and run it by using the following command:
npm run watch
This single-line configuration will bundle your scripts and styles (you can import style.css in script.js) and output the files in the dist directory but this doesn't allow advance configuration for ESBuild like outputting a different name for your stylesheet and script files or using plugins.
Problems:
How to configure ESBuild using an external config file?
ESBuild doesn't support SCSS out-of-the-box. How to configure external plugins like esbuild-sass-plugin and to go even further, how to setup PostCSS and its plugins like Autoprefixer?
How to setup dev server with auto-rebuild?
How to setup PurgeCSS?
Solutions:
1. How to configure ESBuild using an external config file?
Create a new file in root: esbuild.js with the following contents:
import esbuild from "esbuild";
esbuild
.build({
entryPoints: ["src/styles/style.css", "src/scripts/script.js"],
outdir: "dist",
bundle: true,
plugins: [],
})
.then(() => console.log("⚡ Build complete! ⚡"))
.catch(() => process.exit(1));
Add the following code in your package.json:
{
...
"scripts": {
...
"build": "node esbuild.js"
},
...
}
Run the build by using npm run build command and this would bundle up your stylesheets and scripts and output them in dist directory.
For more details and/or adding custom build options, please refer to ESBuild's Build API documentation.
2. ESBuild doesn't support SCSS out-of-the-box. How to configure external plugins like esbuild-sass-plugin and to go even further, how to setup PostCSS and plugins like Autoprefixer?
Install npm dependencies: npm i -D esbuild-sass-plugin postcss autoprefixer
Edit your esbuild.js to the following code:
import esbuild from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
// Generate CSS/JS Builds
esbuild
.build({
entryPoints: ["src/styles/style.scss", "src/scripts/script.js"],
outdir: "dist",
bundle: true,
metafile: true,
plugins: [
sassPlugin({
async transform(source) {
const { css } = await postcss([autoprefixer]).process(source);
return css;
},
}),
],
})
.then(() => console.log("⚡ Build complete! ⚡"))
.catch(() => process.exit(1));
3. How to setup dev server with auto-rebuild?
ESBuild has a limitation on this end, you can either pass in watch: true or run its server. It doesn't allow both.
ESBuild also has another limitation, it doesn't have HMR support like Webpack does.
So to live with both limitations and still allowing a server, we can use Live Server. Install it using npm i -D #compodoc/live-server.
Create a new file in root: esbuild_watch.js with the following contents:
import liveServer from '#compodoc/live-server';
import esbuild from 'esbuild';
import { sassPlugin } from 'esbuild-sass-plugin';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
// Turn on LiveServer on http://localhost:7000
liveServer.start({
port: 7000,
host: 'localhost',
root: '',
open: true,
ignore: 'node_modules',
wait: 0,
});
// Generate CSS/JS Builds
esbuild
.build({
logLevel: 'debug',
metafile: true,
entryPoints: ['src/styles/style.scss', 'src/scripts/script.js'],
outdir: 'dist',
bundle: true,
watch: true,
plugins: [
sassPlugin({
async transform(source) {
const { css } = await postcss([autoprefixer]).process(
source
);
return css;
},
}),
],
})
.then(() => console.log('⚡ Styles & Scripts Compiled! ⚡ '))
.catch(() => process.exit(1));
Edit the scripts in your package.json:
{
...
"scripts": {
...
"build": "node esbuild.js",
"watch": "node esbuild_watch.js"
},
...
}
To run build use this command npm run build.
To run dev server with auto-rebuild run npm run watch. This is a "hacky" way to do things but does a fair-enough job.
4. How to setup PurgeCSS?
I found a great plugin for this: esbuild-plugin-purgecss by peteryuan but it wasn't allowing an option to be passed for the html/views paths that need to be parsed so I
created esbuild-plugin-purgecss-2 that does the job. To set it up, read below:
Install dependencies npm i -D esbuild-plugin-purgecss-2 glob-all.
Add the following code to your esbuild.js and esbuild_watch.js files:
// Import Dependencies
import glob from 'glob-all';
import purgecssPlugin2 from 'esbuild-plugin-purgecss-2';
esbuild
.build({
plugins: [
...
purgecssPlugin2({
content: glob.sync([
// Customize the following URLs to match your setup
'./*.html',
'./views/**/*.html'
]),
}),
],
})
...
Now running the npm run build or npm run watch will purgeCSS from the file paths mentioned in glob.sync([...] in the code above.
TL;DR:
Create an external config file in root esbuild.js and add the command to run it in package.json inside scripts: {..} e.g. "build": "node esbuild.js" to reference and run the config file by using npm run build.
ESBuild doesn't support HMR. Also, you can either watch or serve with ESBuild, not both. To overcome, use a separate dev server library like Live Server.
For the complete setup, please refer to my custom-esbuild-with-scss-purgecss-and-liveserver repository on github.
Final Notes:
I know this is a long thread but it took me a lot of time to figure these out. My intention is to have this here for others looking into the same problems and trying to figure out where to get started.
Thanks.
Adding to Arslan's terrific answer, you can use the PurgeCSS plug-in for postcss to totally eliminate Step 4.
First, install the postcss-purgecss package: npm install #fullhuman/postcss-purgecss
Then, replace the code from Step 2 in Arslan's answer with the code shown below (which eliminates the need for Step 4).
import esbuild from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import purgecss from "#fullhuman/postcss-purgecss";
// Generate CSS/JS Builds
esbuild
.build({
entryPoints: [
"roomflows/static/sass/project.scss",
"roomflows/static/js/project.js",
],
outdir: "dist",
bundle: true,
loader: {
".png": "dataurl",
".woff": "dataurl",
".woff2": "dataurl",
".eot": "dataurl",
".ttf": "dataurl",
".svg": "dataurl",
},
plugins: [
sassPlugin({
async transform(source) {
const { css } = await postcss([
purgecss({
content: ["roomflows/templates/**/*.html"],
}),
autoprefixer,
]).process(source, {
from: "roomflows/static/sass/project.scss",
});
return css;
},
}),
],
minify: true,
metafile: true,
sourcemap: true,
})
.then(() => console.log("⚡ Build complete! ⚡"))
.catch(() => process.exit(1));

Webpack resolve alias and compile file under that alias

I have project which uses lerna ( monorepo, multiple packages ). Few of the packages are standalone apps.
What I want to achieve is having aliases on few of the packages to have something like dependency injection. So for example I have alias #package1/backendProvider/useCheckout and in webpack in my standalone app I resolve it as ../../API/REST/useCheckout . So when I change backend provider to something else I would only change it in webpack.
Problem
Problem appears when this alias is used by some other package ( not standalone app ). For example:
Directory structure looks like this:
Project
packageA
ComponentA
packageB
API
REST
useCheckout
standalone app
ComponentA is in packageA
useCheckout is in packageB under /API/REST/useCheckout path
ComponentA uses useCheckout with alias like import useCheckout from '#packageA/backendProvider/useCheckout
Standalone app uses componentA
The error I get is that Module not found: Can't resolve '#packageA/backendProvider/useCheckout
However when same alias is used in standalone app ( that has webpack with config provided below ) it is working. Problem occurs only for dependencies.
Potential solutions
I know that one solution would be to compile each package with webpack - but that doesn't really seem friendly. What I think is doable is to tell webpack to resolve those aliases to directory paths and then to recompile it. First part ( resolving aliases ) is done.
Current code
As I'm using NextJS my webpack config looks like this:
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
// Fixes npm packages that depend on `fs` module
config.node = {
fs: "empty"
};
const aliases = {
...
"#package1/backendProvider": "../../API/REST/"
};
Object.keys(aliases).forEach(alias => {
config.module.rules.push({
test: /\.(js|jsx)$/,
include: [path.resolve(__dirname, aliases[alias])],
use: [defaultLoaders.babel]
});
config.resolve.alias[alias] = path.resolve(__dirname, aliases[alias]);
});
return config;
}
You don’t need to use aliases. I have a similar setup, just switch to yarn (v1) workspaces which does a pretty smart trick, it adds sym link to all of your packages in the root node_modules.
This way, each package can import other packages without any issue.
In order to apply yarn workspaces with lerna:
// lerna.json
{
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/**"
],
}
// package.json
{
...
"private": true,
"workspaces": [
"packages/*",
]
...
}
This will enable yarn workspace with lerna.
The only think that remains to solve is to make consumer package to transpile the required package (since default configs of babel & webpack is to ignore node_module transpilation).
In Next.js project it is easy, use next-transpile-modules.
// next.config.js
const withTM = require('next-transpile-modules')(['somemodule', 'and-another']); // pass the modules you would like to see transpiled
module.exports = withTM();
In other packages that are using webpack you will need to instruct webpack to transpile your consumed packages (lets assume that they are under npm scope of #somescope/).
So for example, in order to transpile typescript, you can add additional module loader.
// webpack.config.js
{
...
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
include: /[\\/]node_modules[\\/]#somescope[\\/]/, // <-- instruct to transpile ts files from this path
options: {
allowTsInNodeModules: true, // <- this a specific option of ts-loader
transpileOnly: isDevelopment,
compilerOptions: {
module: 'commonjs',
noEmit: false,
},
},
}
]
}
...
resolve: {
symlinks: false, // <-- important
}
}
If you have css, you will need add a section for css as well.
Hope this helps.
Bonus advantage, yarn workspaces will reduce your node_modules size since it will install duplicate packages (with the same semver version) once!

how to combine all yarn files with gulp

I have yarn up and running, have figured out a bit how it works, and made my inroads into figuring out gulp, after having discovered how to install version 4 instead of the default version that throws deprecation errors.
Now I have installed 3 packages with yarn, and it has downloaded a LOT of dependencies. No problem, one can use a gulp file to combine those into one javascript(or so i'm told)
The only thing is, how do I do that whilst maintaining the yarn dependencies as yarn builds those up? How would I format my gulp task for combining the yarn libaries i've added?
My gulp task currently looks like this:
//Concatenate & Minify JS
gulp.task('scripts', function() {
return gulp.src('assets/javascript/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('assets/dist'))
.pipe(rename('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('assets/dist/js'));
});
And this concatenates my scripts as it should, but when I wanted to add the yarn folder it hit me that yarn manages dependencies and what not so everything has it's correct dependency and such. I doubt I can just add them all to the same file and hope all is well.(or can I?)
I run this task with yarn run watch
I've added the following packages: html5shiv, jquery, modernizr
What would be the correct way to add the yarn files in in assets/node_modules?
After long searching I found https://pawelgrzybek.com/using-webpack-with-gulpjs/
which gave me the following solution:
Execute the command:
sudo yarn add global gulp webpack webpack-stream babel-core babel-loader babel-preset-latest
create a webpack.config.js file
enter in it:
module.exports = {
output: {
filename: 'bundle.js', // or whatever you want the filename to be
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: [
['latest', { modules: false }],
],
},
},
],
},
};
Then create a gulpfile.js
var gulp = require('gulp');
var webpack = require('webpack');
var webpackStream = require('webpack-stream');
var webpackConfig = require('./webpack.config.js');
gulp.task('watch', watchTask);
gulp.task('default', defaultTask);
gulp.task('scripts', function() {
return gulp.src('assets/javascript/*.js')
.pipe(webpackStream(webpackConfig), webpack)
.pipe(gulp.dest('./assets/js')); // Or whereever you want your js file to end up.
});
function watchTask(done) {
// Wherever you stored your javascript files
gulp.watch('assets/javascript/*.js', gulp.parallel('scripts'))
done();
}
function defaultTask(done) {
// place code for your default task here
done();
}
Then in the directory execute yarn watch and have it run in the background where you can throw an eye on it now and then.

How to import module from npmjs as Typescript not Javascript instead of using require [Ionic]

As I mention, I want to import a module but I don't understand the document.
I'm using Ionic for develop an app.
I install the module :
npm install wordnet
Instead of using ..
var wordnet = require('wordnet');
wordnet.lookup('define', function(err, definitions) {
definitions.forEach(function(definition) {
console.log(' words: %s', words.trim());
console.log(' %s', definition.glossary);
});
});
How to use the module in the Typescript file for using it function.. as
import { wordnet } from 'wordnet'
Do I need to import module in app.module.ts or in the page page.module.ts or something...?
It depends on your setup. If you're using AngularCLI, then it should find the TypeScript / JavaScript code automagically. Check your node-modules directory to make sure the code is there. If not add the --save-dev flag when you install:
npm install --save-dev wordnet
IF this library relies on binary assets or CSS files, then you may have to edit the angular-cli.json file to tell it where to find image or CSS files. Here is a snippet from the AngularCLI conversion I did in my Learn With books that shows how I set up assets and CSS.
"apps": [
{
"assets": [
"img",
{
"glob": "**/*",
"input": "../node_modules/#swimlane/ngx-datatable/release/assets/fonts",
"output": "./fonts"
}
],
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.css",
"../node_modules/#swimlane/ngx-datatable/release/assets/icons.css",
"../node_modules/#swimlane/ngx-datatable/release/themes/material.css"
],
}
],
If you're using SystemJS to load modules then you'll have to set up wordnet in your SystemJS config. Generally something like this:
(function (global) {
System.config({
map: {
'wordnet': 'path/to/wordnet/code'
},
});
})(this);

ENOENT no such file or directory when trying to load ForerunnerDB with requirejs

Hello I am using yo ko a knockout yeoman generator in my application. The application has been scaffold with requirejs and gulp, but I am having trouble adding ForerunnerDB to the require.config for distribution,
here is the require.config.js:
//require.js looks for the following global when initializing
var require = {
baseUrl: ".",
paths: {
"bootstrap": "bower_modules/components-bootstrap/js/bootstrap.min",
"crossroads": "bower_modules/crossroads/dist/crossroads.min",
"hasher": "bower_modules/hasher/dist/js/hasher.min",
"jquery": "bower_modules/jquery/dist/jquery",
"knockout": "bower_modules/knockout/dist/knockout",
"knockout-projections": "bower_modules/knockout-projections/dist/knockout-projections",
"signals": "bower_modules/js-signals/dist/signals.min",
"text": "bower_modules/requirejs-text/text",
'forerunner': 'bower_modules/forerunnerdb/js/dist/fdb-all.min'
},
shim: {
"bootstrap": { deps: ["jquery"] }
}
};
I am using the gulpfile.js with gulp:serve:dist but I am getting
[Error: Error: ENOENT: no such file or directory, open 'c:...\temp\core.js'
In module tree: app/startup forerunner at Error (native)
But everything is working when I use gulp serve:src.
I already tried to add core.js dependencies in the shim, but can not make it work. There is always a file missing .
here is the github repo
For some reason requirejs gets upset in this configuration so the way to solve it is to add ForerunnerDB to your index.html as a separate script, remove all the dependency references to ForerunnerDB in your require.config.js and then modify your gulp default task to concatenate the scripts.js file that gets generated with the fdb-all.min.js file in ForerunnerDB's js/dists folder.
I have updated the github repo with the changes you have to make as described above. You can see them here: https://github.com/jeanPokou/project_beta/commits/master
When you tried with the shim are you sure you wrote it the right way ?
var require = {
baseUrl: ".",
paths: {
"corejs": "bower_modules/...",
'forerunner': 'bower_modules/forerunnerdb/js/dist/fdb-all.min'
},
shim: {
"corejs": { deps: ["forerunner"] }
}
};

Categories